diff --git a/.github/workflows/ci-autowrap.yaml b/.github/workflows/ci-autowrap.yaml index 9f5d3a2..017af39 100644 --- a/.github/workflows/ci-autowrap.yaml +++ b/.github/workflows/ci-autowrap.yaml @@ -15,24 +15,24 @@ jobs: fail-fast: false matrix: include: -# - CYTHON: "<=0.29.21" -# python-version: "2.7" - - CYTHON: "<=0.29.21" - python-version: "3.7" - CYTHON: "<=0.29.21" python-version: "3.9" # Cython < 0.29.21 not compatible with 3.10, so neither are we -# - CYTHON: ">0.29.21" -# python-version: "2.7" - - CYTHON: ">0.29.21" - python-version: "3.7" - CYTHON: ">0.29.21" python-version: "3.10" - - CYTHON: "==3.0.0a10" - python-version: "3.7" - - CYTHON: "==3.0.0a10" + - CYTHON: "==3.0.0" python-version: "3.10" + - CYTHON: "==3.0.0" + python-version: "3.11" + - CYTHON: "==3.0.0" + python-version: "3.12" + - CYTHON: "==3.1.0" + python-version: "3.10" + - CYTHON: "==3.1.0" + python-version: "3.11" + - CYTHON: "==3.1.0" + python-version: "3.12" steps: - - uses: actions/checkout@v3 + - uses: actions/checkout@v4 - name: Set up Python ${{ matrix.python-version }} uses: actions/setup-python@v4 @@ -45,6 +45,9 @@ jobs: - name: Install pytest run: | python -m pip install pytest + - name: Install setuptools + run: | + python -m pip install setuptools - name: Upgrade cython version run: | python -m pip install "Cython${{ matrix.CYTHON }}" diff --git a/.github/workflows/release-autowrap.yaml b/.github/workflows/release-autowrap.yaml index 8335c3b..72d2dd9 100644 --- a/.github/workflows/release-autowrap.yaml +++ b/.github/workflows/release-autowrap.yaml @@ -15,12 +15,12 @@ jobs: build_publish: runs-on: ubuntu-latest steps: - - uses: actions/checkout@v3 + - uses: actions/checkout@v4 - name: Set up Python uses: actions/setup-python@v4 with: - python-version: 3.9 + python-version: 3.11 - name: Upgrade pip version run: | @@ -30,6 +30,10 @@ jobs: run: | python -m pip install wheel + - name: Install setuptools + run: | + python -m pip install setuptools + - name: Build wheel and source distribution run: | python setup.py bdist_wheel sdist @@ -46,12 +50,12 @@ jobs: id: version - name: Create github release - uses: softprops/action-gh-release@v1 + uses: softprops/action-gh-release@v2 id: create_release with: draft: false prerelease: false - release_name: ${{ steps.version.outputs.version }} + name: ${{ steps.version.outputs.version }} tag_name: release/${{ steps.version.outputs.version }} body_path: CHANGELOG.md env: diff --git a/autowrap/Code.py b/autowrap/Code.py index e0d346c..3deb459 100644 --- a/autowrap/Code.py +++ b/autowrap/Code.py @@ -37,22 +37,6 @@ import string import re -try: - unicode = unicode -except NameError: - # 'unicode' is undefined, must be Python 3 - str = str - unicode = str - bytes = bytes - basestring = (str, bytes) -else: - # 'unicode' exists, must be Python 2 - str = str - unicode = unicode - bytes = str - basestring = basestring - - class Code(object): def __init__(self): self.content: List[Union[Code, str]] = [] @@ -74,7 +58,7 @@ def add(self, what: Union[str, bytes, Code], *a, **kw) -> Code: kw.update(a[0]) if "self" in kw: del kw["self"] # self causes problems in substitute call below - if isinstance(what, basestring): + if isinstance(what, (str, bytes)): try: res = string.Template(what).substitute(**kw) except ValueError: @@ -92,7 +76,7 @@ def add(self, what: Union[str, bytes, Code], *a, **kw) -> Code: def _render(self, _indent="") -> List[str]: result = [] for content in self.content: - if isinstance(content, basestring): + if isinstance(content, (str, bytes)): result.append(_indent + content) else: newindent = _indent + " " * 4 diff --git a/autowrap/CodeGenerator.py b/autowrap/CodeGenerator.py index 8711154..ede2af3 100644 --- a/autowrap/CodeGenerator.py +++ b/autowrap/CodeGenerator.py @@ -84,23 +84,6 @@ def namespace_handler(ns): return ns - -try: - unicode = unicode -except NameError: - # 'unicode' is undefined, must be Python 3 - str = str - unicode = str - bytes = bytes - basestring = (str, bytes) -else: - # 'unicode' exists, must be Python 2 - str = str - unicode = unicode - bytes = str - basestring = basestring - - def augment_arg_names(method): """replaces missing arg_names with "in_%d" % i, where i is the position number of the arg""" @@ -1276,7 +1259,7 @@ def _create_wrapper_for_attribute(self, attribute): ) indented = Code() - if isinstance(cleanup, basestring): + if isinstance(cleanup, (str, bytes)): cleanup = " %s" % cleanup indented.add(cleanup) @@ -1288,10 +1271,10 @@ def _create_wrapper_for_attribute(self, attribute): cy_type = self.cr.cython_type(t) - if isinstance(to_py_code, basestring): + if isinstance(to_py_code, (str, bytes)): to_py_code = " %s" % to_py_code - if isinstance(access_stmt, basestring): + if isinstance(access_stmt, (str, bytes)): access_stmt = " %s" % access_stmt if t.is_ptr: @@ -1360,7 +1343,7 @@ def create_wrapper_for_nonoverloaded_method(self, cdcl, py_name, method): else: indented = meth_code - if isinstance(full_call_stmt, basestring): + if isinstance(full_call_stmt, (str, bytes)): indented.add( """ | $full_call_stmt @@ -1373,14 +1356,14 @@ def create_wrapper_for_nonoverloaded_method(self, cdcl, py_name, method): for cleanup in reversed(cleanups): if not cleanup: continue - if isinstance(cleanup, basestring): + if isinstance(cleanup, (str, bytes)): cleanup = " %s" % cleanup indented.add(cleanup) to_py_code = out_converter.output_conversion(res_t, "_r", "py_result") if to_py_code is not None: # for non void return value - if isinstance(to_py_code, basestring): + if isinstance(to_py_code, (str, bytes)): to_py_code = " %s" % to_py_code indented.add(to_py_code) indented.add(" return py_result") @@ -1455,7 +1438,7 @@ def _create_wrapper_for_free_function( out_converter = self.cr.get(res_t) full_call_stmt = out_converter.call_method(res_t, cy_call_str) - if isinstance(full_call_stmt, basestring): + if isinstance(full_call_stmt, (str, bytes)): fun_code.add( """ | $full_call_stmt @@ -1468,7 +1451,7 @@ def _create_wrapper_for_free_function( for cleanup in reversed(cleanups): if not cleanup: continue - if isinstance(cleanup, basestring): + if isinstance(cleanup, (str, bytes)): cleanup = " %s" % cleanup fun_code.add(cleanup) @@ -1476,7 +1459,7 @@ def _create_wrapper_for_free_function( out_vars = ["py_result"] if to_py_code is not None: # for non void return value - if isinstance(to_py_code, basestring): + if isinstance(to_py_code, (str, bytes)): to_py_code = " %s" % to_py_code fun_code.add(to_py_code) fun_code.add(" return %s" % (", ".join(out_vars))) @@ -1573,7 +1556,7 @@ def create_wrapper_for_nonoverloaded_constructor(self, class_decl, py_name, cons for cleanup in reversed(cleanups): if not cleanup: continue - if isinstance(cleanup, basestring): + if isinstance(cleanup, (str, bytes)): cleanup = " %s" % cleanup cons_code.add(cleanup) @@ -1699,7 +1682,7 @@ def create_special_getitem_method(self, mdcl): out_converter = self.cr.get(res_t) full_call_stmt = out_converter.call_method(res_t, cy_call_str) - if isinstance(full_call_stmt, basestring): + if isinstance(full_call_stmt, (str, bytes)): meth_code.add( """ | $full_call_stmt @@ -1712,14 +1695,14 @@ def create_special_getitem_method(self, mdcl): for cleanup in reversed(cleanups): if not cleanup: continue - if isinstance(cleanup, basestring): + if isinstance(cleanup, (str, bytes)): cleanup = Code().add(cleanup) meth_code.add(cleanup) out_var = "py_result" to_py_code = out_converter.output_conversion(res_t, "_r", out_var) if to_py_code is not None: # for non void return value - if isinstance(to_py_code, basestring): + if isinstance(to_py_code, (str, bytes)): to_py_code = " %s" % to_py_code meth_code.add(to_py_code) meth_code.add(" return $out_var", locals()) @@ -1827,7 +1810,7 @@ def create_cast_methods(self, mdecls): call_stmt = "<%s>(deref(self.inst.get()))" % cy_t full_call_stmt = out_converter.call_method(res_t, call_stmt) - if isinstance(full_call_stmt, basestring): + if isinstance(full_call_stmt, (str, bytes)): code.add( """ | $full_call_stmt @@ -1838,7 +1821,7 @@ def create_cast_methods(self, mdecls): code.add(full_call_stmt) to_py_code = out_converter.output_conversion(res_t, "_r", "py_res") - if isinstance(to_py_code, basestring): + if isinstance(to_py_code, (str, bytes)): to_py_code = " %s" % to_py_code code.add(to_py_code) code.add(""" return py_res""") diff --git a/autowrap/ConversionProvider.py b/autowrap/ConversionProvider.py index df60fef..6528d69 100644 --- a/autowrap/ConversionProvider.py +++ b/autowrap/ConversionProvider.py @@ -40,22 +40,6 @@ import logging as L import string -try: - unicode = unicode -except NameError: - # 'unicode' is undefined, must be Python 3 - str = str - unicode = str - bytes = bytes - basestring = (str, bytes) -else: - # 'unicode' exists, must be Python 2 - str = str - unicode = unicode - bytes = str - basestring = basestring - - def mangle(s): s = s.replace("(", "_l_") s = s.replace(")", "_r_") @@ -2036,7 +2020,7 @@ def input_conversion( # Cython understands it and uses the Py_IsUnicodeCheck code.add( """ - |if isinstance($argument_var, unicode): + |if isinstance($argument_var, str): | $argument_var = $argument_var.encode('utf-8') """, locals(), @@ -2222,7 +2206,7 @@ def __contains__(self, cpp_type): return False def cython_type(self, type_: Union[CppType, AnyStr]) -> CppType: - if isinstance(type_, basestring): + if isinstance(type_, (str, bytes)): type_ = CppType(type_) return type_.transformed(self.instance_mapping) diff --git a/autowrap/DeclResolver.py b/autowrap/DeclResolver.py index c3375be..a9fab26 100644 --- a/autowrap/DeclResolver.py +++ b/autowrap/DeclResolver.py @@ -37,7 +37,6 @@ import autowrap.Utils as Utils import os from collections import defaultdict -from autowrap.tools import OrderKeepingDictionary __doc__ = """ @@ -170,7 +169,7 @@ class ResolvedClass(object): """ name: str - methods: OrderKeepingDictionary + methods: dict attributes: List[ResolvedAttribute] cpp_decl: PXDParser.CppClassDecl ns: AnyStr @@ -185,7 +184,7 @@ class ResolvedClass(object): def __init__(self, name, methods, attributes, decl, instance_map, local_map): self.name: str = name # resolve overloads - self.methods: OrderKeepingDictionary = OrderKeepingDictionary() + self.methods: dict = {} for m in methods: self.methods.setdefault(m.name, []).append(m) self.attributes = attributes diff --git a/autowrap/PXDParser.py b/autowrap/PXDParser.py index 469373c..61017d4 100644 --- a/autowrap/PXDParser.py +++ b/autowrap/PXDParser.py @@ -53,7 +53,6 @@ from autowrap.Code import Code as Code from collections import defaultdict -from .tools import OrderKeepingDictionary AnnotDict = Dict[str, Union[bool, List[str]]] @@ -143,7 +142,7 @@ def _parse_multiline_annotations(lines: Collection[str]) -> AnnotDict: # make sure wrap-doc is always a Code object if "wrap-doc" in result.keys(): doc = result.get("wrap-doc", []) - if isinstance(doc, basestring): + if isinstance(doc, (str, bytes)): doc = [doc] c = Code() @@ -208,7 +207,7 @@ def parse_line_annotations(node: Cython.Compiler.Nodes.Node, lines: Sequence[str # make sure wrap-doc is always a Code object if "wrap-doc" in result.keys(): doc = result.get("wrap-doc", []) - if isinstance(doc, basestring): + if isinstance(doc, (str, bytes)): doc = [doc] c = Code() @@ -401,7 +400,7 @@ def parseTree(cls, node: Cython.Compiler.Nodes.CppClassNode, lines: Collection[s template_parameters = [t[0] for t in template_parameters] class_annotations = parse_class_annotations(node, lines) - methods = OrderKeepingDictionary() + methods = dict() attributes = [] for att in node.attributes: decl = None @@ -583,10 +582,10 @@ def parse_pxd_file(path, warn_level=1): options, sources = parse_command_line(["--cplus", path]) - import pkg_resources + from importlib.resources import files # TODO sync with CodeGenerator.py function fixed_include_dirs - data = pkg_resources.resource_filename("autowrap", "data_files/autowrap") + data = files("autowrap").joinpath("data_files/autowrap") options.include_path = [data] options.language_level = sys.version_info.major diff --git a/autowrap/tools.py b/autowrap/tools.py deleted file mode 100644 index b046f5a..0000000 --- a/autowrap/tools.py +++ /dev/null @@ -1,90 +0,0 @@ -# encoding: utf-8 - -__license__ = """ - -Copyright (c) 2012-2014, Uwe Schmitt, all rights reserved. - -Redistribution and use in source and binary forms, with or without -modification, are permitted provided that the following conditions are met: - -Redistributions of source code must retain the above copyright notice, this -list of conditions and the following disclaimer. - -Redistributions in binary form must reproduce the above copyright notice, this -list of conditions and the following disclaimer in the documentation and/or -other materials provided with the distribution. - -Neither the name of the ETH Zurich nor the names of its contributors may be -used to endorse or promote products derived from this software without specific -prior written permission. - -THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND -ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED -WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE -DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE -FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL -DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR -SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER -CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, -OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE -OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -""" - - -# for backwards compatibility with python 2.6 we use our own implementation -# of an OrderedDictionary, which is not feature complete but simple and -# sufficient for our uses - - -class OrderKeepingDictionary(object): - def __init__(self): - self._dd = dict() - self._keys = [] - - def __setitem__(self, k, v): - if k not in self._keys: - self._keys.append(k) - self._dd[k] = v - - def __getitem__(self, k): - return self._dd[k] - - def update(self): - raise NotImplementedError("update not implemented") - - def iterkeys(self): - for k in self._keys: - yield k - - __iter__ = iterkeys - - def itervalues(self): - for k in self._keys: - yield self._dd[k] - - def iteritems(self): - for k in self._keys: - yield (k, self._dd[k]) - - def keys(self): - return list(self.iterkeys()) - - def values(self): - return list(self.itervalues()) - - def items(self): - return list(self.iteritems()) - - def get(self, k, default=None): - return self._dd.get(k, default) - - def setdefault(self, k, default): - if k not in self._keys: - self._keys.append(k) - return self._dd.setdefault(k, default) - - def __delitem__(self, k): - raise NotImplementedError("__delitem__ not implemented") - - def __len__(self): - return len(self._dd) diff --git a/setup.py b/setup.py index 0e7c081..48c12da 100644 --- a/setup.py +++ b/setup.py @@ -6,37 +6,33 @@ except ImportError: pass -from setuptools import find_packages, setup +from setuptools import find_namespace_packages, setup versionfile = "autowrap/version.py" -try: - execfile(versionfile) -except: - exec(open(versionfile).read()) +exec(open(versionfile).read()) setup( name="autowrap", version="%d.%d.%d" % __version__, - maintainer="The OpenMS team and Uwe Schmitt", - maintainer_email="uschmitt@mineway.de", - license="http://opensource.org/licenses/BSD-3-Clause", + maintainer="OpenMS Inc.", + maintainer_email="webmaster@openms.de", + license="BSD-3-Clause", platforms=["any"], description="Generates Python Extension modules from commented Cython PXD files", classifiers=[ "Development Status :: 3 - Alpha", "Intended Audience :: Developers", "License :: OSI Approved :: BSD License", - "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" + "Programming Language :: Python :: 3.11", + "Programming Language :: Python :: 3.12", "Topic :: Software Development :: Code Generators", ], long_description=""" autowrap automatically generates python extension modules for wrapping C++ libraries based on annotated (commented) cython pxd files. """, - packages=find_packages(exclude=["ez_setup", "examples", "tests"]), + packages=find_namespace_packages(include=['autowrap*'], exclude=['autowrap.data_files.examples', 'autowrap.data_files.tests', 'autowrap.data_files.ez_setup']), include_package_data=True, # see MANIFEST.in zip_safe=False, test_suite="nose.collector", diff --git a/tests/test_decl_resolver.py b/tests/test_decl_resolver.py index 5f3e02e..69055bd 100644 --- a/tests/test_decl_resolver.py +++ b/tests/test_decl_resolver.py @@ -792,7 +792,7 @@ def test_class_and_enum(): ) assert A.name == "A" - (method,) = A.methods.values()[0] + (method,) = list(A.methods.values())[0] assert method.name == "A" assert len(method.arguments) == 0 diff --git a/tests/test_pxd_parser.py b/tests/test_pxd_parser.py index f29c34a..47689ba 100644 --- a/tests/test_pxd_parser.py +++ b/tests/test_pxd_parser.py @@ -344,7 +344,7 @@ def test_multi_classes_in_one_file(): ) assert inst1.name == "A" assert inst1.template_parameters == ["B", "C"] - assert inst1.methods.keys() == ["run"] + assert list(inst1.methods.keys()) == ["run"] assert inst2.name == "C" assert inst2.template_parameters == [ "E",