diff --git a/.azure_pipelines/run_tests.yml b/.azure_pipelines/run_tests.yml new file mode 100644 index 0000000..3174d1f --- /dev/null +++ b/.azure_pipelines/run_tests.yml @@ -0,0 +1,56 @@ +parameters: + name: "" + python: "" + architecture: "x64" + kind: "native" + +jobs: +- job: ${{ format('{0}_{1}_{2}', parameters.name, parameters.architecture, parameters.kind) }} + dependsOn: PyLint + pool: + vmImage: "vs2017-win2016" + + steps: + # ensure the required Python versions are available + - task: UsePythonVersion@0 + displayName: setup python + inputs: + versionSpec: ${{ parameters.python }} + architecture: ${{ parameters.architecture }} + + - script: "python -c \"import sys; print(sys.version); print(sys.executable)\"" + displayName: show python information + + - script: | + python -m pip install --upgrade pip + pip install -U setuptools + displayName: upgrade pip and setuptools + + - script: | + pip install -r requirements.txt + displayName: Install dependencies + + - ${{ if eq(parameters.kind, 'cython') }}: + - script: | + pip install -r build_requirements.txt + displayName: Install build requirements + + - script: | + pip install -U wheel + python setup.py bdist_wheel + pip install --pre --no-index -f dist threaded + displayName: Build and install + + - script: | + pip install pytest pytest-sugar + + pytest -vvv --junitxml=unit_result.xml test + displayName: PyTest + + - task: PublishTestResults@2 + displayName: publish test results via junit + condition: succeededOrFailed() + inputs: + testResultsFormat: "JUnit" + testResultsFiles: ${{ format('$(System.DefaultWorkingDirectory)/unit_result.xml') }} + testRunTitle: ${{ format('{0}_{1}_{2}', parameters.name, parameters.architecture, parameters.kind) }} diff --git a/.travis.yml b/.travis.yml index efb8d35..89d7d27 100644 --- a/.travis.yml +++ b/.travis.yml @@ -150,6 +150,7 @@ jobs: tags: true distributions: sdist skip_upload_docs: true + skip_existing: true cache: pip before_cache: diff --git a/azure-pipelines.yml b/azure-pipelines.yml new file mode 100644 index 0000000..ec31b8d --- /dev/null +++ b/azure-pipelines.yml @@ -0,0 +1,121 @@ +# Python package +# Create and test a Python package on multiple Python versions. +# Add steps that analyze code, save the dist with the build record, publish to a PyPI-compatible index, and more: +# https://docs.microsoft.com/azure/devops/pipelines/languages/python + +jobs: +- job: 'PyLint' + pool: + vmIMage: 'VS2017-Win2016' + strategy: + maxParallel: 1 + steps: + - task: UsePythonVersion@0 + inputs: + versionSpec: '3.7' + + - script: | + python -m pip install --upgrade pip + pip install -U setuptools + pip install -r requirements.txt + pip install -r CI_REQUIREMENTS.txt + displayName: 'Install dependencies' + + - script: | + pip install -U pylint + pylint -f msvs threaded + displayName: 'PyLint' + +- template: .azure_pipelines/run_tests.yml + parameters: {name: 'Python_34', python: '3.4', architecture: 'x64', kind: 'native'} +- template: .azure_pipelines/run_tests.yml + parameters: {name: 'Python_35', python: '3.5', architecture: 'x64', kind: 'native'} +- template: .azure_pipelines/run_tests.yml + parameters: {name: 'Python_36', python: '3.6', architecture: 'x64', kind: 'native'} +- template: .azure_pipelines/run_tests.yml + parameters: {name: 'Python_37', python: '3.7', architecture: 'x64', kind: 'native'} + +- template: .azure_pipelines/run_tests.yml + parameters: {name: 'Python_34', python: '3.4', architecture: 'x64', kind: 'cython'} +- template: .azure_pipelines/run_tests.yml + parameters: {name: 'Python_34', python: '3.4', architecture: 'x86', kind: 'cython'} +- template: .azure_pipelines/run_tests.yml + parameters: {name: 'Python_35', python: '3.5', architecture: 'x64', kind: 'cython'} +- template: .azure_pipelines/run_tests.yml + parameters: {name: 'Python_35', python: '3.5', architecture: 'x86', kind: 'cython'} +- template: .azure_pipelines/run_tests.yml + parameters: {name: 'Python_36', python: '3.6', architecture: 'x64', kind: 'cython'} +- template: .azure_pipelines/run_tests.yml + parameters: {name: 'Python_36', python: '3.6', architecture: 'x86', kind: 'cython'} +- template: .azure_pipelines/run_tests.yml + parameters: {name: 'Python_37', python: '3.7', architecture: 'x64', kind: 'cython'} +- template: .azure_pipelines/run_tests.yml + parameters: {name: 'Python_37', python: '3.7', architecture: 'x86', kind: 'cython'} + +- job: 'Build_and_deploy' + dependsOn: + - Python_34_x64_native + - Python_35_x64_native + - Python_36_x64_native + - Python_37_x64_native + + - Python_34_x64_cython + - Python_34_x86_cython + - Python_35_x64_cython + - Python_35_x86_cython + - Python_36_x64_cython + - Python_36_x86_cython + - Python_37_x64_cython + - Python_37_x86_cython + condition: and(succeeded(), startsWith(variables['Build.SourceBranch'], 'refs/tags/')) + pool: + vmIMage: 'VS2017-Win2016' + strategy: + maxParallel: 6 + matrix: + Python34_x64: + python.version: '3.4' + Python34_x86: + python.version: '3.4' + python.architecture: 'x86' + Python35_x64: + python.version: '3.5' + Python35_x86: + python.version: '3.5' + python.architecture: 'x86' + Python36_x64: + python.version: '3.6' + Python36_x86: + python.version: '3.6' + python.architecture: 'x86' + Python37_x64: + python.version: '3.7' + Python37_x86: + python.version: '3.7' + python.architecture: 'x86' + + steps: + - task: UsePythonVersion@0 + inputs: + versionSpec: '$(python.version)' + architecture: '$(python.architecture)' + + - script: | + python -m pip install --upgrade pip + pip install -U setuptools + pip install -r build_requirements.txt + displayName: 'Install dependencies' + + - script: | + python setup.py bdist_wheel + displayName: 'Build' + + - task: TwineAuthenticate@0 + displayName: 'Twine Authenticate ' + inputs: + externalFeeds: PyPI + + - script: | + pip install -U twine + twine upload -r PyPI --config-file $(PYPIRC_PATH) dist/Advanced_Descriptors-* --skip-existing + displayName: 'Deploy' diff --git a/doc/source/conf.py b/doc/source/conf.py index 5524299..fc0dbdb 100644 --- a/doc/source/conf.py +++ b/doc/source/conf.py @@ -13,110 +13,10 @@ # All configuration values have a default; values that are commented out # serve to show the default. -import ast -import collections -import os.path -import sys - -PY3 = sys.version_info[:2] > (2, 7) -PY34 = sys.version_info[:2] > (3, 3) - -with open( - os.path.join( - os.path.dirname(__file__), '..', '..', - 'threaded', '__init__.py' - ) -) as f: - source = f.read() - - -# noinspection PyUnresolvedReferences -def get_simple_vars_from_src(src): - """Get simple (string/number/boolean and None) assigned values from source. - - :param src: Source code - :type src: str - :returns: OrderedDict with keys, values = variable names, values - :rtype: typing.Dict[ - str, - typing.Union[ - str, bytes, - int, float, complex, - list, set, dict, tuple, - None, - ] - ] - - Limitations: Only defined from scratch variables. - Not supported by design: - * Imports - * Executable code, including string formatting and comprehensions. - - Examples: - - >>> string_sample = "a = '1'" - >>> get_simple_vars_from_src(string_sample) - OrderedDict([('a', '1')]) - - >>> int_sample = "b = 1" - >>> get_simple_vars_from_src(int_sample) - OrderedDict([('b', 1)]) - - >>> list_sample = "c = [u'1', b'1', 1, 1.0, 1j, None]" - >>> result = get_simple_vars_from_src(list_sample) - >>> result == collections.OrderedDict( - ... [('c', [u'1', b'1', 1, 1.0, 1j, None])] - ... ) - True - - >>> iterable_sample = "d = ([1], {1: 1}, {1})" - >>> get_simple_vars_from_src(iterable_sample) - OrderedDict([('d', ([1], {1: 1}, {1}))]) - - >>> multiple_assign = "e = f = g = 1" - >>> get_simple_vars_from_src(multiple_assign) - OrderedDict([('e', 1), ('f', 1), ('g', 1)]) - """ - ast_data = ( - ast.Str, ast.Num, - ast.List, ast.Set, ast.Dict, ast.Tuple - ) - if PY3: - ast_data += (ast.Bytes,) - if PY34: - ast_data += (ast.NameConstant,) - - tree = ast.parse(src) - - result = collections.OrderedDict() - - for node in ast.iter_child_nodes(tree): - if not isinstance(node, ast.Assign): # We parse assigns only - continue - try: - if isinstance(node.value, ast_data): - value = ast.literal_eval(node.value) - elif isinstance( # NameConstant in python < 3.4 - node.value, ast.Name - ) and isinstance( - node.value.ctx, ast.Load # Read constant - ): - value = ast.literal_eval(node.value) - else: - continue - except ValueError: - continue - for tgt in node.targets: - if isinstance( - tgt, ast.Name - ) and isinstance( - tgt.ctx, ast.Store - ): - result[tgt.id] = value - return result - - -variables = get_simple_vars_from_src(source) +import pkg_resources + +release = pkg_resources.get_distribution("threaded").version +version = '.'.join(release.split('.')[:2]) # If extensions (or modules to document with autodoc) are in another directory, # add these directories to sys.path here. If the directory is relative to the @@ -168,9 +68,9 @@ def get_simple_vars_from_src(src): # built documents. # # The short X.Y version. -version = variables['__version__'] + # The full version, including alpha/beta/rc tags. -release = variables['__version__'] + # The language for content autogenerated by Sphinx. Refer to documentation # for a list of supported languages. diff --git a/setup.py b/setup.py index 44e62be..7a9569d 100644 --- a/setup.py +++ b/setup.py @@ -21,6 +21,12 @@ import os.path import shutil import sys +try: + import typing +except ImportError: + typing = None + +import setuptools try: # noinspection PyPackageRequirements @@ -31,7 +37,6 @@ except ImportError: gevent = cythonize = None -import setuptools with open(os.path.join(os.path.dirname(__file__), "threaded", "__init__.py")) as f: source = f.read() @@ -43,7 +48,7 @@ long_description = f.read() -def _extension(modpath): +def _extension(modpath: str) -> setuptools.Extension: """Make setuptools.Extension.""" return setuptools.Extension(modpath, [modpath.replace(".", "/") + ".py"]) @@ -122,7 +127,9 @@ def build_extension(self, ext): # noinspection PyUnresolvedReferences -def get_simple_vars_from_src(src): +def get_simple_vars_from_src( + src: str +) -> "typing.Dict[str, typing.Union[str, bytes, int, float, complex, list, set, dict, tuple, None]]": """Get simple (string/number/boolean and None) assigned values from source. :param src: Source code @@ -219,7 +226,6 @@ def get_simple_vars_from_src(src): "{name} <{email}>".format(name=name, email=email) for name, email in variables["__maintainers__"].items() ), url=variables["__url__"], - version=variables["__version__"], license=variables["__license__"], description=variables["__description__"], long_description=long_description, @@ -232,9 +238,13 @@ def get_simple_vars_from_src(src): # situations as progressive releases of projects are done. # Blacklist setuptools 34.0.0-34.3.2 due to https://github.com/pypa/setuptools/issues/951 # Blacklist setuptools 36.2.0 due to https://github.com/pypa/setuptools/issues/1086 - setup_requires="setuptools >= 21.0.0,!=24.0.0," - "!=34.0.0,!=34.0.1,!=34.0.2,!=34.0.3,!=34.1.0,!=34.1.1,!=34.2.0,!=34.3.0,!=34.3.1,!=34.3.2," - "!=36.2.0", + setup_requires=[ + "setuptools >= 21.0.0,!=24.0.0," + "!=34.0.0,!=34.0.1,!=34.0.2,!=34.0.3,!=34.1.0,!=34.1.1,!=34.2.0,!=34.3.0,!=34.3.1,!=34.3.2," + "!=36.2.0", + "setuptools_scm", + ], + use_scm_version=True, extras_require={"gevent": ["gevent >= 1.2.2"]}, install_requires=required, package_data={"threaded": ["py.typed"]}, diff --git a/threaded/__init__.py b/threaded/__init__.py index c81ece8..96b63c3 100644 --- a/threaded/__init__.py +++ b/threaded/__init__.py @@ -16,6 +16,8 @@ import typing +import pkg_resources + # pylint: disable=no-name-in-module from ._asynciotask import AsyncIOTask, asynciotask from ._threaded import Threaded, threaded @@ -28,6 +30,17 @@ GThreadPooled = gthreadpooled = None # type: ignore # pylint: enable=no-name-in-module +try: + __version__ = pkg_resources.get_distribution(__name__).version +except pkg_resources.DistributionNotFound: + # package is not installed, try to get from SCM + try: + import setuptools_scm # type: ignore + + __version__ = setuptools_scm.get_version() + except ImportError: + pass + __all__ = ( "ThreadPooled", @@ -41,7 +54,6 @@ if GThreadPooled is not None: # pragma: no cover __all__ += ("GThreadPooled", "gthreadpooled") -__version__ = "2.1.0" __author__ = "Alexey Stepanov" __author_email__ = "penguinolog@gmail.com" __maintainers__ = { diff --git a/threaded/_threaded.pxd b/threaded/_threaded.pxd index 7518f5c..ca986ea 100644 --- a/threaded/_threaded.pxd +++ b/threaded/_threaded.pxd @@ -17,10 +17,6 @@ Asyncio is supported """ -import functools -import threading -import typing - from threaded cimport class_decorator cpdef tuple __all__ diff --git a/tools/build-wheels.sh b/tools/build-wheels.sh index 032245f..a8e8e81 100755 --- a/tools/build-wheels.sh +++ b/tools/build-wheels.sh @@ -57,6 +57,7 @@ for PYTHON in ${PYTHON_VERSIONS}; do done find /io/dist/ -type f -not -name "*$package_name*" -delete +rm -rf /io/.eggs rm -rf /io/build rm -rf /io/*.egg-info rm -rf /io/.pytest_cache