Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 3 additions & 3 deletions .github/workflows/test.yml
Original file line number Diff line number Diff line change
Expand Up @@ -11,10 +11,10 @@ on:

jobs:
tests:
runs-on: ubuntu-20.04
runs-on: ubuntu-24.04
strategy:
matrix:
python-version: [3.6, 3.7, 3.8, 3.9]
python-version: ['3.9', '3.10', '3.11']

steps:
- uses: actions/checkout@v2
Expand All @@ -27,7 +27,7 @@ jobs:
- name: Set up Poetry
uses: snok/install-poetry@v1
with:
version: 1.1.10
version: 1.8.1

- name: Install dependencies
run: |
Expand Down
7 changes: 7 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,12 @@
# Changelog

## [1.14.0] - 2025-08-27

- %help works by passing user-agent via headers (#426).
- Graphics work for Stata 17+ (#428).
- Use `importlib.resources` insteadof `pkg_resources` (#420).
- Autocompletions work on notebooks (pass empty 'metadata' field internally).

## [1.12.3] - 2021-02-25

- Simplify kernel installation for conda environments
Expand Down
2,917 changes: 1,963 additions & 954 deletions poetry.lock

Large diffs are not rendered by default.

43 changes: 30 additions & 13 deletions pyproject.toml
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
[tool.poetry]
name = "stata_kernel"
version = "1.13.0"
version = "1.14.0"
description = "A Jupyter kernel for Stata. Works with Windows, macOS, and Linux."
authors = [
"Kyle Barron <kylebarron2@gmail.com>",
Expand All @@ -19,38 +19,55 @@ classifiers = [
'Operating System :: Microsoft :: Windows',
'Operating System :: POSIX :: Linux',
'Operating System :: Unix',
'Programming Language :: Python :: 3.6',
'Programming Language :: Python :: 3.7',
'Programming Language :: Python :: 3.8',
'Programming Language :: Python :: 3.9',
'Programming Language :: Python :: 3.10',
'Programming Language :: Python :: 3.11',
]
keywords = ["stata"]
repository = "https://github.com/kylebarron/stata_kernel"
documentation = "https://kylebarron.dev/stata_kernel/"

[tool.poetry.dependencies]
python = "^3.6.1"
python = "^3.9.0"
beautifulsoup4 = "^4.6.3"
pandas = "^1.0"
pandas = ">=1.0"
numpy = ">=1.22.0"
pygments = "^2.2"
pexpect = "^4.6.0"
requests = "^2.19.1"
jupyter-client = "^5.2.3"
IPython = "^7.16.3"
ipykernel = "^4.8.2"
packaging = "^17.1"
pillow = ">=5.2.0"
pywin32 = { version = ">=223", platform = "Windows" }
fake-useragent = "^2.0.0"
IPython = "^7.34"
ipykernel = "^4.8.2"
jupyter-client = "^6.1.12"
jupyter-core = "^5.8.1"
notebook = "^6.5.7"
nbclient = "^0.8"
setuptools = "<81"

[tool.poetry.dev-dependencies]
bumpversion = "^0.6"
mkdocs-material = ">=3.0.3"
mkdocs = ">=1.0"
yapf = ">=0.20.2"
pytest = ">=3.7.1"
# importlib-metadata==0.23
# jupyter_kernel_test==0.3.0
jupyter_kernel_test = "0.3.0"

[build-system]
requires = ["poetry-core>=1.1.10"]
requires = ["poetry-core>=1.8.1"]
build-backend = "poetry.core.masonry.api"

[tool.bumpver]
current_version = "1.14.0"
version_pattern = "MAJOR.MINOR.PATCH"
commit = true
tag = true
commit_message = "bump version {old_version} → {new_version}"
tag_message = "{new_version}"

[tool.bumpver.file_patterns]
"setup.cfg" = ['current_version = {version}']
"stata_kernel/kernel.py" = ["implementation_version = '{version}'"]
"stata_kernel/__init__.py" = ["__version__ = '{version}'"]
"pyproject.toml" = ['version = "{version}"']
2 changes: 1 addition & 1 deletion setup.cfg
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
[bumpversion]
current_version = 1.13.0
current_version = 1.14.0
commit = True
tag = True

Expand Down
2 changes: 1 addition & 1 deletion stata_kernel/__init__.py
Original file line number Diff line number Diff line change
@@ -1,3 +1,3 @@
"""An example Jupyter kernel"""

__version__ = '1.13.0'
__version__ = '1.14.0'
1 change: 1 addition & 0 deletions stata_kernel/docs/make_href
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@

from bs4 import BeautifulSoup as bs
import urllib
import urllib.request
import re
import os

Expand Down
4 changes: 2 additions & 2 deletions stata_kernel/install.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@
from shutil import copyfile
from pathlib import Path
from textwrap import dedent
from pkg_resources import resource_filename
from importlib.resources import files
from IPython.utils.tempdir import TemporaryDirectory
from jupyter_client.kernelspec import KernelSpecManager

Expand All @@ -31,7 +31,7 @@ def install_my_kernel_spec(user=True, prefix=None):
json.dump(kernel_json, f, sort_keys=True)

# Copy logo to tempdir to be installed with kernelspec
logo_path = resource_filename('stata_kernel', 'docs/logo-64x64.png')
logo_path = files('stata_kernel').joinpath('docs/logo-64x64.png')
copyfile(logo_path, os.path.join(td, 'logo-64x64.png'))

print('Installing Jupyter kernel spec')
Expand Down
18 changes: 9 additions & 9 deletions stata_kernel/kernel.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@
from textwrap import dedent
from datetime import datetime
from xml.etree import ElementTree as ET
from pkg_resources import resource_filename
from importlib.resources import files
from ipykernel.kernelbase import Kernel

from .config import config
Expand All @@ -22,7 +22,7 @@

class StataKernel(Kernel):
implementation = 'stata_kernel'
implementation_version = '1.13.0'
implementation_version = '1.14.0'
language = 'stata'
language_info = {
'name': 'stata',
Expand All @@ -38,13 +38,12 @@ class StataKernel(Kernel):
def __init__(self, *args, **kwargs):
# Copy syntax highlighting files
from_paths = [
Path(resource_filename('stata_kernel', 'pygments/stata.py')),
Path(resource_filename('stata_kernel', 'codemirror/stata.js'))]
Path(files('stata_kernel').joinpath('pygments/stata.py')),
Path(files('stata_kernel').joinpath('codemirror/stata.js'))]
to_paths = [
Path(resource_filename('pygments', 'lexers/stata.py')),
Path(files('pygments').joinpath('lexers/stata.py')),
Path(
resource_filename(
'notebook',
files('notebook').joinpath(
'static/components/codemirror/mode/stata/stata.js'))]

for from_path, to_path in zip(from_paths, to_paths):
Expand Down Expand Up @@ -355,6 +354,7 @@ def do_complete(self, code, cursor_pos):
self.sc_delimit_mode, self.stata.mata_mode)

return {
'metadata': {},
'status': 'ok',
'cursor_start': pos,
'cursor_end': cursor_pos,
Expand Down Expand Up @@ -400,7 +400,7 @@ def cleanTail(self, tail, rprompt):
fh.seek(pos + 1, os.SEEK_SET)
fh.truncate()

def do_inspect(self, code, cursor_pos, detail_level=0):
def do_inspect(self, code, cursor_pos, detail_level=0, metadata={}):
inspect_keyword = re.compile(
r'\b(?P<keyword>\w+)\(?\s*$', flags=re.MULTILINE).search

Expand Down Expand Up @@ -445,6 +445,6 @@ def do_inspect(self, code, cursor_pos, detail_level=0):
'found': found,
# data can be empty if nothing is found
'data': data,
'metadata': {}}
'metadata': metadata}

return content
4 changes: 2 additions & 2 deletions stata_kernel/pygments/stata.py
Original file line number Diff line number Diff line change
Expand Up @@ -143,9 +143,9 @@ class StataLexer(RegexLexer):
],
# Built in functions and statements
'keywords': [
(words(builtins_functions, prefix = r'\b', suffix = r'(?=\()'),
(words(builtins_functions, prefix=r'\b', suffix=r'(?=\()'),
Name.Function),
(words(builtins_base, prefix = r'(^\s*|\s)', suffix = r'\b'),
(words(builtins_base, prefix=r'(^\s*|\s)', suffix=r'\b'),
Keyword),
],
# http://www.stata.com/help.cgi?operators
Expand Down
22 changes: 12 additions & 10 deletions stata_kernel/stata_magics.py
Original file line number Diff line number Diff line change
@@ -1,11 +1,13 @@
import sys
import re
import urllib
import urllib.request
import pandas as pd
from fake_useragent import UserAgent
from textwrap import dedent
from bs4 import BeautifulSoup as bs
from argparse import ArgumentParser, SUPPRESS
from pkg_resources import resource_filename
from importlib.resources import files

from .config import config
from .code_manager import CodeManager
Expand Down Expand Up @@ -196,14 +198,11 @@ class StataMagics():
# 'time',
# 'timeit'

csshelp_default = resource_filename(
'stata_kernel', 'css/_StataKernelHelpDefault.css')
help_kernel_html = resource_filename('stata_kernel', 'docs/index.html')
help_kernel_plain = resource_filename('stata_kernel', 'docs/index.txt')
help_magics_html = resource_filename(
'stata_kernel', 'docs/using_stata_kernel/magics.html')
help_magics_plain = resource_filename(
'stata_kernel', 'docs/using_stata_kernel/magics.txt')
csshelp_default = files('stata_kernel').joinpath('css/_StataKernelHelpDefault.css')
help_kernel_html = files('stata_kernel').joinpath('docs/index.html')
help_kernel_plain = files('stata_kernel').joinpath('docs/index.txt')
help_magics_html = files('stata_kernel').joinpath('docs/using_stata_kernel/magics.html')
help_magics_plain = files('stata_kernel').joinpath('docs/using_stata_kernel/magics.txt')

def __init__(self, kernel):
self.quit_early = None
Expand Down Expand Up @@ -601,8 +600,11 @@ def magic_help(self, code, kernel):
return ''

cmd = scode.replace(" ", "_")
url = self.html_help.format(cmd)
try:
reply = urllib.request.urlopen(self.html_help.format(cmd))
headers = {'User-Agent': UserAgent().random}
req = urllib.request.Request(url, headers=headers)
reply = urllib.request.urlopen(req)
html = reply.read().decode("utf-8")
soup = bs(html, 'html.parser')

Expand Down
11 changes: 5 additions & 6 deletions stata_kernel/stata_session.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@
# from timeit import default_timer
from pathlib import Path
from textwrap import dedent
from pkg_resources import resource_filename
from importlib.resources import files

from .utils import check_stata_kernel_updated_version
from .config import config
Expand Down Expand Up @@ -97,8 +97,7 @@ def __init__(self, kernel):
# Stata
# -----

adofile = resource_filename(
'stata_kernel', 'ado/_StataKernelCompletions.ado')
adofile = files('stata_kernel').joinpath('ado/_StataKernelCompletions.ado')
adodir = Path(adofile).resolve().parent
init_cmd = """\
adopath + `"{0}"\'
Expand All @@ -117,7 +116,7 @@ def __init__(self, kernel):
self.do(dedent(init_cmd), md5='finished_init_cmd', display=False)
try:
rc, res = self.do(
'di "`c(stata_version)\'"\n`done\'', md5='done', display=False)
'di "`c(stata_version)\'"\n`done\'', md5='done', display=False)
self.stata_version = res
isold = int(res[:2]) < 15
if (platform.system() == 'Windows') and isold:
Expand Down Expand Up @@ -424,7 +423,7 @@ def expect_graph(self, child, res):
res = '(' + res

regex = r'^\((note: )?file {}/graph\d+\.({}) not found\)'.format(
self.cache_dir_str, '|'.join(self.kernel.graph_formats))
self.cache_dir_str, '|'.join(self.kernel.graph_formats))
if re.search(regex, res):
return None
else:
Expand Down Expand Up @@ -626,7 +625,7 @@ def _mata_escape(self, line):
if self.mata_open:
strfmt = 'stata(`"{0}"\')'
return '\n'.join([
strfmt.format(l) if l else l for l in line.split('\n')])
strfmt.format(ll) if ll else ll for ll in line.split('\n')])
else:
return line

Expand Down
15 changes: 7 additions & 8 deletions stata_kernel/utils.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
import os
import re
import requests
import json
import urllib
import urllib.request
import platform

from shutil import which
Expand All @@ -11,8 +13,8 @@

def check_stata_kernel_updated_version(stata_kernel_version):
try:
r = requests.get('https://pypi.org/pypi/stata-kernel/json')
pypi_v = r.json()['info']['version']
r = urllib.request.urlopen('https://pypi.org/pypi/stata-kernel/json')
pypi_v = json.load(r)['info']['version']
if version.parse(pypi_v) > version.parse(stata_kernel_version):
msg = """\
NOTE: A newer version of stata_kernel exists. Run
Expand All @@ -22,7 +24,7 @@ def check_stata_kernel_updated_version(stata_kernel_version):
to install the latest version.
"""
return dedent(msg)
except requests.exceptions.RequestException:
except (urllib.error.HTTPError, urllib.error.URLError):
return


Expand All @@ -46,10 +48,7 @@ def find_path():
def win_find_path():
import winreg
reg = winreg.ConnectRegistry(None, winreg.HKEY_CLASSES_ROOT)
subkeys = [
r'Stata16Do\shell\do\command', r'Stata15Do\shell\do\command',
r'Stata14Do\shell\do\command', r'Stata13Do\shell\do\command',
r'Stata12Do\shell\do\command']
subkeys = [rf'Stata{ver}Do\shell\do\command' for ver in [12, 13, 14, 15, 16, 17, 18, 19]]

fpath = ''
for subkey in subkeys:
Expand Down
6 changes: 3 additions & 3 deletions tests/test_kernel_completions.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
from utils import StataKernelTestFramework
from stata_kernel.config import Config


class TestKernelCompletions(StataKernelTestFramework):

# samples for the autocompletion functionality
Expand All @@ -26,10 +27,10 @@ class TestKernelCompletions(StataKernelTestFramework):
'text': '%set au',
'matches': {x for x in Config().all_settings if x.startswith('au')}
}
] # yapf: disable
] # yapf: disable

def test_stata_global_completion(self):
code = f"""\
code = """\
global abcd "helloworld"
global abdef "foo"
global aef "bar"
Expand Down Expand Up @@ -115,7 +116,6 @@ def test_stata_path_completion12(self):
matches = {'${datadir}/completions.py', '${datadir}/code_manager.py', '${datadir}/config.py'}
self._test_completion(text, matches, exact=False)


def test_stata_varlist_completion(self):
self._run()
text = 'list '
Expand Down
1 change: 1 addition & 0 deletions tests/test_kernel_display_data.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
from utils import StataKernelTestFramework


class TestKernelDisplayData(StataKernelTestFramework):
# Samples of code which should generate a rich display output, and
# the expected MIME type
Expand Down
Loading