diff --git a/.github/workflows/code.yml b/.github/workflows/code.yml index fe6c928e..3d90a6c2 100644 --- a/.github/workflows/code.yml +++ b/.github/workflows/code.yml @@ -27,12 +27,7 @@ jobs: fail-fast: false matrix: os: [ubuntu-latest, windows-latest, macos-latest] - python: [cp27, cp36, cp37, cp38, cp39] - - exclude: - # No cothread or asyncio for windows python 2.7 so doesn't work - - os: windows-latest - python: cp27 + python: [cp36, cp37, cp38, cp39] include: # Put coverage in the project directory for mac diff --git a/.vscode/settings.json b/.vscode/settings.json index 61c6dc1c..f21c98de 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -7,4 +7,6 @@ "python.testing.unittestEnabled": false, "python.testing.pytestEnabled": true, "python.languageServer": "Pylance", + "files.trimTrailingWhitespace": true, + "files.trimFinalNewlines": true, } diff --git a/CHANGELOG.rst b/CHANGELOG.rst index 1675bebd..dbf9a58c 100644 --- a/CHANGELOG.rst +++ b/CHANGELOG.rst @@ -9,7 +9,9 @@ and this project adheres to `Semantic Versioning ` 3.2.1_ - 2021-11-25 ------------------- @@ -20,7 +22,7 @@ Changed: Added: -- Provide logging for exceptions raised in callbacks +- Provide logging for exceptions raised in callbacks Fixed: diff --git a/setup.cfg b/setup.cfg index 94ab6b1a..26faed7f 100644 --- a/setup.cfg +++ b/setup.cfg @@ -10,14 +10,14 @@ long_description_content_type = text/x-rst classifiers = Development Status :: 5 - Production/Stable License :: OSI Approved :: Apache Software License - Programming Language :: Python :: 2.7 + Programming Language :: Python :: 3.6 Programming Language :: Python :: 3.7 Programming Language :: Python :: 3.8 Programming Language :: Python :: 3.9 [options] packages = softioc -python_requires = >=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*, !=3.5.* +python_requires = >=3.6 [options.entry_points] # Include a command line script @@ -39,11 +39,11 @@ useful = aioca >=1.3 # Dev and docs dependencies dev = - pytest-cov + pytest-cov pytest-flake8 - sphinx-rtd-theme-github-versions; python_version > "3.0" - pytest-asyncio; python_version > "3.0" - aioca >=1.3 ; python_version > "3.0" + sphinx-rtd-theme-github-versions + pytest-asyncio + aioca >=1.3 cothread; sys_platform != "win32" p4p @@ -57,8 +57,8 @@ extend-ignore = [tool:pytest] # Run pytest with all our checkers, and don't spam us with massive tracebacks on error # Don't do flake8 here as we need to separate it out for CI -addopts = - --tb=native -vv --doctest-modules --ignore=iocStats --ignore=epicscorelibs --ignore=docs +addopts = + --tb=native -vv --doctest-modules --ignore=iocStats --ignore=epicscorelibs --ignore=docs --cov=softioc --cov-report term --cov-report xml:cov.xml [coverage:run] diff --git a/softioc/device_core.py b/softioc/device_core.py index ab2ce92f..da81c5d1 100644 --- a/softioc/device_core.py +++ b/softioc/device_core.py @@ -124,10 +124,9 @@ class DSET(DSET_BASE): setattr(dset, method_name, callback) # Hang onto the values we publish to EPICS to ensure that they persist! - # We also need to ensure that the device name persits. + # We also need to ensure that the device name persists. cls.__dset = dset - if sys.version_info >= (3,): - cls._device_name_ = cls._device_name_.encode() + cls._device_name_ = cls._device_name_.encode() imports.registryDeviceSupportAdd(cls._device_name_, byref(cls.__dset)) diff --git a/softioc/extension.c b/softioc/extension.c index c04ff630..3ab7b2c4 100644 --- a/softioc/extension.c +++ b/softioc/extension.c @@ -13,11 +13,6 @@ #include #include -/* In Python3 this function has been renamed. */ -#if PY_MAJOR_VERSION >= 3 -#define PyInt_FromLong(value) PyLong_FromLong(value) -#endif - /* Reference stealing version of PyDict_SetItemString */ static void set_dict_item_steal( PyObject *dict, const char *name, PyObject *py_value) @@ -28,7 +23,7 @@ static void set_dict_item_steal( /* Helper for function below. */ #define ADD_ENUM(dict, name) \ - set_dict_item_steal(dict, #name, PyInt_FromLong(name)) + set_dict_item_steal(dict, #name, PyLong_FromLong(name)) /* Alas, EPICS has changed the numerical assignments of the DBF_ enums between * versions, so to avoid unpleasant surprises, we compute thes values here in C @@ -226,7 +221,7 @@ static struct PyMethodDef softioc_methods[] = { {NULL, NULL, 0, NULL} /* Sentinel */ }; -#if PY_MAJOR_VERSION >= 3 + static struct PyModuleDef softioc_module = { PyModuleDef_HEAD_INIT, "softioc._extension", @@ -234,19 +229,8 @@ static struct PyModuleDef softioc_module = { -1, softioc_methods, }; -#endif - -#if PY_MAJOR_VERSION >= 3 -# define PyMOD(NAME) PyObject* PyInit_##NAME (void) -#else -# define PyMOD(NAME) void init##NAME (void) -#endif -PyMOD(_extension) +PyObject *PyInit__extension(void) { -#if PY_MAJOR_VERSION >= 3 return PyModule_Create(&softioc_module); -#else - Py_InitModule("softioc._extension", softioc_methods); -#endif } diff --git a/softioc/fields.py b/softioc/fields.py index 103b74ab..d44dfb69 100644 --- a/softioc/fields.py +++ b/softioc/fields.py @@ -66,18 +66,6 @@ } -if sys.version_info >= (3,): - def decode(string): - return string.decode() - def encode(string): - return string.encode() -else: - def decode(string): - return string - def encode(string): - return string - - class RecordFactory(object): def __init__(self, record_type, fields): '''Uses the EPICS static database to discover the offset in the record @@ -110,9 +98,9 @@ def __getattr__(self, field): if field == 'TIME': return self.__get_time(address) elif field_type == DBF_STRING: - return decode(string_at(cast(address, c_char_p))) + return string_at(cast(address, c_char_p)).decode() elif field_type in [DBF_INLINK, DBF_OUTLINK]: - return decode(cast(address, POINTER(c_char_p))[0]) + return cast(address, POINTER(c_char_p))[0].decode() else: ctypes_type = DbfCodeToCtypes[field_type] return cast(address, POINTER(ctypes_type))[0] @@ -124,7 +112,7 @@ def __setattr__(self, field, value): if field == 'TIME': self.__set_time(address, value) elif field_type == DBF_STRING: - value = encode(str(value)) + value = str(value).encode() buffer = create_string_buffer(value) if size > len(value) + 1: size = len(value) + 1 diff --git a/softioc/imports.py b/softioc/imports.py index 71b59dbd..eb525fb8 100644 --- a/softioc/imports.py +++ b/softioc/imports.py @@ -34,27 +34,15 @@ def expect_true(status, function, args): assert status, 'Expected True' -if sys.version_info < (3,): - # Python 2 - auto_encode = c_char_p - def auto_decode(result, func, args): - return result - -else: - # Python 3 - - # Encode all strings to c_char_p - class auto_encode(c_char_p): - encoded = [] - @classmethod - def from_param(cls, value): - if value is None: - return value - else: - return value.encode() - - def auto_decode(result, func, args): - return result.decode() + +# Encode all strings to c_char_p +class auto_encode(c_char_p): + @classmethod + def from_param(cls, value): + if value is None: + return value + else: + return value.encode() # int registryDeviceSupportAdd( diff --git a/softioc/softioc.py b/softioc/softioc.py index 216ebf83..c9214142 100644 --- a/softioc/softioc.py +++ b/softioc/softioc.py @@ -310,14 +310,8 @@ def interactive_ioc(context = {}, call_exit = True): exports = dict((key, globals()[key]) for key in command_names) import code - if sys.version_info < (3, 6): - interact_args = {} - else: - # This suppresses irritating exit message introduced by Python3. Alas, - # this option is only available in Python 3.6! - interact_args = dict(exitmsg = '') try: - code.interact(local = dict(exports, **context), **interact_args) + code.interact(local = dict(exports, **context), exitmsg = '') except SystemExit as e: if call_exit: safeEpicsExit(e.code) diff --git a/tests/conftest.py b/tests/conftest.py index 11d5afff..3479fe5c 100644 --- a/tests/conftest.py +++ b/tests/conftest.py @@ -7,12 +7,6 @@ import pytest -if sys.version_info < (3,): - # Python2 has no asyncio, so ignore these tests - collect_ignore = [ - "test_asyncio.py", "sim_asyncio_ioc.py", "sim_asyncio_ioc_override.py" - ] - class SubprocessIOC: def __init__(self, ioc_py): self.pv_prefix = "".join(