diff --git a/.github/workflows/build-release-artifacts.yaml b/.github/workflows/build-release-artifacts.yaml new file mode 100644 index 0000000..dd17bf2 --- /dev/null +++ b/.github/workflows/build-release-artifacts.yaml @@ -0,0 +1,38 @@ +name: Build Release Artifacts + +on: + release: + types: [created] + +jobs: + build-and-publish: + name: Build and Release Artifacts + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v2 + - name: Get release information + id: get_release + uses: bruceadams/get-release@v1.2.2 + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + - name: Set up Python 3 + uses: actions/setup-python@v2 + with: + python-version: 3.x + - name: Install dependencies + run: | + python -m pip install --upgrade pip + python -m pip install ply sphinx sphinx sphinx_rtd_theme + - name: Build manpage + run: python -m sphinx -b man docs dist + - name: Archive manpage + run: tar -czvf manpage.tar.gz -C dist pyxtuml.1 + - name: Upload Release Asset + uses: actions/upload-release-asset@v1 + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + with: + upload_url: ${{ steps.get_release.outputs.upload_url }} + asset_path: manpage.tar.gz + asset_name: manpage.tar.gz + asset_content_type: application/gzip diff --git a/.github/workflows/publish-pypi-release.yaml b/.github/workflows/publish-pypi-release.yaml new file mode 100644 index 0000000..8e1661e --- /dev/null +++ b/.github/workflows/publish-pypi-release.yaml @@ -0,0 +1,26 @@ +# See: https://packaging.python.org/guides/publishing-package-distribution-releases-using-github-actions-ci-cd-workflows/ + +name: Publish Source Packages to PyPI + +on: + release: + types: [created] + +jobs: + build-and-publish: + name: Build and Publish Source Packages + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@master + - name: Set up Python 3 + uses: actions/setup-python@v1 + with: + python-version: 3.x + - name: Install pypa/build + run: python -m pip install build --user + - name: Build a binary wheel and a source tarball + run: python -m build --sdist --wheel --outdir dist/ . + - name: Publish distribution to Test PyPI + uses: pypa/gh-action-pypi-publish@master + with: + password: ${{ secrets.PYPI_API_TOKEN }} diff --git a/.github/workflows/publish-test-results.yaml b/.github/workflows/publish-test-results.yaml new file mode 100644 index 0000000..735e6ac --- /dev/null +++ b/.github/workflows/publish-test-results.yaml @@ -0,0 +1,37 @@ +name: Unit Test Results + +on: + workflow_run: + workflows: ['Run Unit Tests'] + types: + - completed + +jobs: + unit-test-results: + name: Publish Unit Test Results + runs-on: ubuntu-latest + if: github.event.workflow_run.conclusion != 'skipped' + + steps: + - name: Download and Extract Artifacts + env: + GITHUB_TOKEN: ${{secrets.GITHUB_TOKEN}} + run: | + mkdir -p artifacts && cd artifacts + + artifacts_url=${{ github.event.workflow_run.artifacts_url }} + + gh api "$artifacts_url" -q '.artifacts[] | [.name, .archive_download_url] | @tsv' | while read artifact + do + IFS=$'\t' read name url <<< "$artifact" + gh api $url > "$name.zip" + unzip -d "$name" "$name.zip" + done + + - name: Publish Unit Test Results + uses: EnricoMi/publish-unit-test-result-action@v1 + with: + commit: ${{ github.event.workflow_run.head_sha }} + event_file: artifacts/Event File/event.json + event_name: ${{ github.event.workflow_run.event }} + files: "artifacts/**/*.xml" diff --git a/.github/workflows/run-tests.yaml b/.github/workflows/run-tests.yaml new file mode 100644 index 0000000..4558683 --- /dev/null +++ b/.github/workflows/run-tests.yaml @@ -0,0 +1,52 @@ +name: Run Unit Tests + +on: + push: + branches: + - master + pull_request: + release: + +jobs: + + build-and-test: + strategy: + matrix: + os: [ubuntu-latest, windows-latest] + python-version: [2.7, 3.x, pypy2, pypy3] + exclude: + # excludes pypy 3 on Windows + - os: windows-latest + python-version: pypy3 + fail-fast: false + runs-on: ${{ matrix.os }} + steps: + - uses: actions/checkout@v2 + - name: Set up Python ${{ matrix.python-version }} + uses: actions/setup-python@v2 + with: + python-version: ${{ matrix.python-version }} + - name: Install dependencies + run: | + python -m pip install --upgrade pip + pip install -r requirements.txt + - name: Build pyxtuml + run: python setup.py build + - name: Run Unit Tests + run: pytest tests --doctest-modules --junitxml=test-results/test-results-${{ matrix.python-version }}.xml + - name: Upload Unit Test Results + if: always() + uses: actions/upload-artifact@v2 + with: + name: Unit Test Results (Python ${{ matrix.python-version }}) + path: test-results/test-results-${{ matrix.python-version }}.xml + + event_file: + name: "Event File" + runs-on: ubuntu-latest + steps: + - name: Upload + uses: actions/upload-artifact@v2 + with: + name: Event File + path: ${{ github.event_path }} diff --git a/.github/workflows/test-coverage.yaml b/.github/workflows/test-coverage.yaml new file mode 100644 index 0000000..e90106e --- /dev/null +++ b/.github/workflows/test-coverage.yaml @@ -0,0 +1,30 @@ +name: Run Coverage Analysis + +on: + push: + branches: + - master + pull_request: + release: + +jobs: + coverage: + name: Coverage + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v2 + - name: Set up Python + uses: actions/setup-python@v2 + with: + python-version: 2.7 + - name: Install dependencies + run: | + python -m pip install --upgrade pip + pip install -r requirements.txt + pip install coveralls + - name: Run coverage analysis + run: coverage run --source=xtuml,bridgepoint setup.py test + - name: Upload to Coveralls + run: coveralls --service=github + env: + COVERALLS_REPO_TOKEN: ${{ secrets.GITHUB_TOKEN }} diff --git a/.readthedocs.yaml b/.readthedocs.yaml new file mode 100644 index 0000000..3c1c3e8 --- /dev/null +++ b/.readthedocs.yaml @@ -0,0 +1,7 @@ +version: 2 +sphinx: + configuration: docs/conf.py +python: + version: 3.8 + install: + - requirements: docs/requirements.txt diff --git a/.travis.yml b/.travis.yml deleted file mode 100644 index 139a74b..0000000 --- a/.travis.yml +++ /dev/null @@ -1,12 +0,0 @@ -language: python -python: - - "3.6" - - "3.5" - - "2.7" -install: - - pip install ply - - pip install coveralls -script: - - coverage run --source=xtuml,bridgepoint setup.py test -after_success: - - if [[ $TRAVIS_PYTHON_VERSION == 2.7 ]]; then coveralls; fi diff --git a/appveyor.yml b/appveyor.yml deleted file mode 100644 index 955bb4e..0000000 --- a/appveyor.yml +++ /dev/null @@ -1,27 +0,0 @@ -build: off - -environment: - matrix: - - PYTHON: "C:\\Python27" - PYTHON_VERSION: "2.7.x" - PYTHON_ARCH: "32" - -init: - - "%PYTHON%/python --version" - -install: - - "%PYTHON%/python -m pip install ply" - - "%PYTHON%/python -m pip install sphinx" - - "%PYTHON%/python -m pip install sphinx_rtd_theme" - -before_test: - - "%PYTHON%/python setup.py build" - -test_script: - - "%PYTHON%/python setup.py test" - -after_test: - - "%PYTHON%/python -m sphinx -b man docs dist" - -artifacts: - - path: dist\* diff --git a/docs/requirements.txt b/docs/requirements.txt new file mode 100644 index 0000000..5ce8bf2 --- /dev/null +++ b/docs/requirements.txt @@ -0,0 +1,4 @@ +sphinx==4.2.0 +sphinx_rtd_theme==1.0.0 +readthedocs-sphinx-search==0.1.1 +ply==3.11 diff --git a/requirements.txt b/requirements.txt index e4f9a48..7729e67 100644 --- a/requirements.txt +++ b/requirements.txt @@ -1 +1,2 @@ -ply \ No newline at end of file +ply +pytest diff --git a/setup.py b/setup.py index ce03391..4bc5cd9 100755 --- a/setup.py +++ b/setup.py @@ -68,9 +68,16 @@ def run(self): sys.exit(exit_code) +from os import path +this_directory = path.abspath(path.dirname(__file__)) +with open(path.join(this_directory, 'README.rst')) as f: + long_description = f.read() + setup(name='pyxtuml', version='2.2.2', # ensure that this is the same as in xtuml.version description='Library for parsing, manipulating, and generating BridgePoint xtUML models', + long_description=long_description, + long_description_content_type='text/x-rst', author=u'John Törnblom', author_email='john.tornblom@gmail.com', url='https://github.com/xtuml/pyxtuml', diff --git a/tests/test_xtuml/test_tools.py b/tests/test_xtuml/test_tools.py index 496935e..06da309 100644 --- a/tests/test_xtuml/test_tools.py +++ b/tests/test_xtuml/test_tools.py @@ -18,6 +18,7 @@ import sys import unittest +import pytest import xtuml.tools @@ -44,7 +45,11 @@ def test_node(self): w.accept(root) - s = sys.stdout.getvalue() + try: + s = sys.stdout.getvalue() + except AttributeError: + s, _ = self.capfd.readouterr() + expected = 'Node\n' expected += ' Node\n' expected += ' Node\n' @@ -57,7 +62,10 @@ def test_generic_class(self): w.accept(self) - s = sys.stdout.getvalue() + try: + s = sys.stdout.getvalue() + except AttributeError: + s, _ = self.capfd.readouterr() self.assertEqual(s, 'TestNodePrinter\n') @@ -67,10 +75,17 @@ def test_none(self): w.accept(None) - s = sys.stdout.getvalue() + try: + s = sys.stdout.getvalue() + except AttributeError: + s, _ = self.capfd.readouterr() self.assertEqual(s, '') + @pytest.fixture(autouse=True) + def capfd(self, capfd): + self.capfd = capfd + class TestIdGenerator(unittest.TestCase): ''' diff --git a/xtuml/load.py b/xtuml/load.py index a272606..4527ab0 100644 --- a/xtuml/load.py +++ b/xtuml/load.py @@ -308,7 +308,7 @@ def _populate_instance_with_positional_arguments(metamodel, stmt): metaclass = metamodel.find_metaclass(stmt.kind) if len(metaclass.attributes) != len(stmt.values): - logger.warn('%s:%d:schema mismatch' % (stmt.filename, stmt.lineno)) + logger.warning('%s:%d:schema mismatch' % (stmt.filename, stmt.lineno)) inst = metamodel.new(stmt.kind) for attr, value in zip(metaclass.attributes, stmt.values): @@ -341,7 +341,7 @@ def _populate_instance_with_named_arguments(metamodel, stmt): inst_unames = [name.upper() for name in stmt.names] if set(inst_unames) - set(schema_unames): - logger.warn('%s:%d:schema mismatch' % (stmt.filename, stmt.lineno)) + logger.warning('%s:%d:schema mismatch' % (stmt.filename, stmt.lineno)) inst = metamodel.new(stmt.kind) for name, ty in metaclass.attributes: diff --git a/xtuml/meta.py b/xtuml/meta.py index 8527ea8..d4c3e4d 100644 --- a/xtuml/meta.py +++ b/xtuml/meta.py @@ -21,7 +21,6 @@ ''' import logging -import collections import xtuml @@ -32,6 +31,14 @@ except ImportError: pass +import collections +try: + # Python 3.x + collectionsAbc = collections.abc +except: + # Python 2.7 + collectionsAbc = collections + logger = logging.getLogger(__name__) @@ -741,7 +748,7 @@ def __init__(self, handle): elif isinstance(handle, Class): handle = [handle] - elif not isinstance(handle, collections.Iterable): + elif not isinstance(handle, collectionsAbc.Iterable): raise MetaException("Unable to navigate across '%s'" % type(handle)) self.handle = handle diff --git a/xtuml/tools.py b/xtuml/tools.py index a5eb631..285c0b9 100644 --- a/xtuml/tools.py +++ b/xtuml/tools.py @@ -15,9 +15,16 @@ # # You should have received a copy of the GNU Lesser General Public # License along with pyxtuml. If not, see . -import collections import uuid +import collections +try: + # Python 3.x + collectionsAbc = collections.abc +except: + # Python 2.7 + collectionsAbc = collections + class IdGenerator(object): ''' @@ -121,7 +128,7 @@ def default_leave(self, node): pass -class OrderedSet(collections.MutableSet): +class OrderedSet(collectionsAbc.MutableSet): ''' Set that remembers original insertion order. '''