Skip to content

Commit eb1587a

Browse files
authored
Merge pull request #67 from ISISComputingGroup/py_313_support
Py 313 support
2 parents be6474d + 4173805 commit eb1587a

File tree

7 files changed

+40
-27
lines changed

7 files changed

+40
-27
lines changed

.github/workflows/lint_and_test.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@ jobs:
1313
strategy:
1414
matrix:
1515
os: ["ubuntu-latest", "windows-latest"]
16-
version: ['3.11', '3.12']
16+
version: ['3.11', '3.12', '3.13']
1717
fail-fast: false
1818
steps:
1919
- uses: actions/checkout@v5

pyproject.toml

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@ classifiers = [
2424
"Intended Audience :: Developers",
2525
"Programming Language :: Python :: 3.11",
2626
"Programming Language :: Python :: 3.12",
27+
"Programming Language :: Python :: 3.13",
2728
]
2829

2930
dependencies = [
@@ -56,7 +57,7 @@ dependencies = [
5657
# - It depends on a couple of heavyweight libs (py4j and tornado) that aren't necessary otherwise
5758
plot = [
5859
# When updating, check plotting works in GUI. Must keep pinned to a specific, tested version.
59-
"matplotlib==3.10.1",
60+
"matplotlib==3.10.7",
6061
# Python <-> Java communication, to spawn matplotlib plots in GUI
6162
"py4j",
6263
# Tornado webserver used by custom backend

src/genie_python/genie.py

Lines changed: 20 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -118,7 +118,7 @@ class _GetbeamlineparsReturn(TypedDict):
118118
print("\ngenie_python version " + VERSION)
119119

120120
MIN_SUPPORTED_PYTHON_VERSION = (3, 11, 0)
121-
MAX_SUPPORTED_PYTHON_VERSION = (3, 12, 999)
121+
MAX_SUPPORTED_PYTHON_VERSION = (3, 13, 999)
122122

123123
if not (MIN_SUPPORTED_PYTHON_VERSION <= sys.version_info[0:3] <= MAX_SUPPORTED_PYTHON_VERSION):
124124
message = (
@@ -1562,16 +1562,26 @@ def __load_module(name: str, directory: str) -> types.ModuleType:
15621562
raise ValueError(f"Cannot find spec for module {name} in {directory}")
15631563
module = importlib.util.module_from_spec(spec)
15641564

1565-
if module.__file__ is None:
1566-
raise ValueError(f"Module {name} has no __file__ attribute")
1565+
err_msg = (
1566+
f"Cannot load script '{name}' as its name clashes with a standard python module "
1567+
f"or with a module accessible elsewhere on the python path.\n"
1568+
f"The conflicting module was '{module}'.\n"
1569+
f"If this is a user script, rename the user script to avoid the clash."
1570+
)
1571+
1572+
try:
1573+
module_file = module.__file__
1574+
except AttributeError:
1575+
raise ValueError(err_msg) from None
1576+
1577+
if module_file is None:
1578+
raise ValueError(err_msg)
1579+
1580+
module_location = str(module_file)
1581+
1582+
if os.path.normpath(os.path.dirname(module_location)) != os.path.normpath(directory):
1583+
raise ValueError(err_msg)
15671584

1568-
if os.path.normpath(os.path.dirname(module.__file__)) != os.path.normpath(directory):
1569-
raise ValueError(
1570-
f"Cannot load script '{name}' as its name clashes with a standard python module "
1571-
f"or with a module accessible elsewhere on the python path.\n"
1572-
f"The conflicting module was at '{module.__file__}'.\n"
1573-
f"If this is a user script, rename the user script to avoid the clash."
1574-
)
15751585
sys.modules[name] = module
15761586
loader = spec.loader
15771587
if loader is None:

src/genie_python/genie_api_setup.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -284,8 +284,8 @@ def __init__(self, original_function: Callable) -> None:
284284
def just_path_on_load(self, text: str, act_tok: Any) -> list[str]:
285285
"""
286286
Returns completions for load on a path if load_script in path otherwise returns as before.
287-
Will replace .metadata\.plugins\org.eclipse.pde.core\.bundle_pool\plugins\
288-
org.python.pydev_5.9.2.201708151115\pysrc\_pydev_bundle\pydev_ipython_console_011.py
287+
Will replace .metadata\\.plugins\\org.eclipse.pde.core\\.bundle_pool\\plugins\\
288+
org.python.pydev_5.9.2.201708151115\\pysrc\\_pydev_bundle\\pydev_ipython_console_011.py
289289
290290
This functions will filter out the pydev completions if the line contains load_script.
291291
It know which are the pydev ones because they are marked with the type '11'.

src/genie_python/scanning_instrument_pylint_plugin.py

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
from typing import TYPE_CHECKING, Any
22

3-
import astroid
43
from astroid import MANAGER
4+
from astroid.nodes import Arguments, ClassDef, FunctionDef, Module
55

66
if TYPE_CHECKING:
77
from pylint.lint import PyLinter
@@ -11,7 +11,7 @@ def register(linter: "PyLinter") -> None:
1111
"""Register the plugin."""
1212

1313

14-
def transform(node: astroid.ClassDef, *args: Any, **kwargs: Any) -> None:
14+
def transform(node: ClassDef, *args: Any, **kwargs: Any) -> None:
1515
"""Add ScanningInstrument methods to the declaring module.
1616
1717
If the given class is derived from ScanningInstrument,
@@ -22,16 +22,16 @@ def transform(node: astroid.ClassDef, *args: Any, **kwargs: Any) -> None:
2222
if node.basenames and "ScanningInstrument" in node.basenames:
2323
public_methods = filter(lambda method: not method.name.startswith("__"), node.methods())
2424
for public_method in public_methods:
25-
if isinstance(node.parent, astroid.Module):
26-
new_func = astroid.FunctionDef(
25+
if isinstance(node.parent, Module):
26+
new_func = FunctionDef(
2727
name=public_method.name,
2828
lineno=0,
2929
col_offset=0,
3030
parent=node.parent,
3131
end_lineno=0,
3232
end_col_offset=0,
3333
)
34-
arguments = astroid.Arguments(
34+
arguments = Arguments(
3535
vararg=None,
3636
kwarg=None,
3737
parent=new_func,
@@ -50,4 +50,4 @@ def transform(node: astroid.ClassDef, *args: Any, **kwargs: Any) -> None:
5050
node.parent.locals[public_method.name] = [new_func]
5151

5252

53-
MANAGER.register_transform(astroid.ClassDef, transform)
53+
MANAGER.register_transform(ClassDef, transform)

tests/test_genie.py

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -34,9 +34,9 @@
3434
)
3535

3636
invalid_module_msg = (
37-
f"Cannot load script test as its name clashes with a standard python module "
37+
f"Cannot load script 'test' as its name clashes with a standard python module "
3838
f"or with a module accessible elsewhere on the python path.\nThe conflicting "
39-
f"module was at '{test.__file__}'.\nIf this is a user script, rename the "
39+
f"module was '{test}'.\nIf this is a user script, rename the "
4040
f"user script to avoid the clash."
4141
)
4242

@@ -60,9 +60,11 @@ def test_GIVEN_script_uses_module_name_WHEN_load_script_THEN_error(self):
6060
script = os.path.join(os.path.abspath(os.path.dirname(__file__)), "test_scripts", "test.py")
6161

6262
# Act
63-
with self.assertRaises(ValueError, msg=invalid_module_msg):
63+
with self.assertRaises(ValueError) as ex:
6464
genie.load_script(script)
6565

66+
self.assertEqual(str(ex.exception), invalid_module_msg)
67+
6668
def test_GIVEN_valid_script_WHEN_load_script_THEN_can_call_script(self):
6769
# Arrange
6870
script = os.path.join(

tests/test_script_checker.py

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,7 @@
2424
import tempfile
2525
import unittest
2626

27-
import astroid
27+
from astroid.nodes import Module
2828

2929
from genie_python.genie_epics_api import API
3030
from genie_python.genie_script_checker import ScriptChecker
@@ -405,17 +405,17 @@ def test_GIVEN_builtin_module_THEN_it_can_safely_be_cached(self):
405405
def test_GIVEN_site_packages_module_THEN_it_can_safely_be_cached(self):
406406
import numpy
407407

408-
mod = astroid.Module(numpy.__name__, numpy.__file__)
408+
mod = Module(numpy.__name__, numpy.__file__)
409409
self.assertTrue(self.checker._can_cache_module("numpy", mod))
410410

411411
def test_GIVEN_user_script_THEN_it_should_not_be_cached(self):
412412
name = "my_user_script"
413-
mod = astroid.Module(name, os.path.join("C:\\", "scripts", "my_user_script.py"))
413+
mod = Module(name, os.path.join("C:\\", "scripts", "my_user_script.py"))
414414
self.assertFalse(self.checker._can_cache_module(name, mod))
415415

416416
def test_GIVEN_instrument_script_THEN_it_should_not_be_cached(self):
417417
name = "my_inst_script"
418-
mod = astroid.Module(
418+
mod = Module(
419419
name,
420420
os.path.join(
421421
"C:\\",

0 commit comments

Comments
 (0)