From 3a53fcc6d995fa016b3c9f84c7d1faf1b5ed606b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ionel=20Cristian=20M=C4=83rie=C8=99?= Date: Sun, 15 Jun 2014 05:14:59 +0300 Subject: [PATCH 001/179] Add appveyor configuration. *cross-fingers* --- .travis.yml | 78 +++--- appveyor.tmpl.yml | 18 ++ appveyor.yml | 30 +++ configure | 8 +- setup.cfg | 4 +- tox.ini | 664 +++++++++++++++++++++------------------------- 6 files changed, 396 insertions(+), 406 deletions(-) create mode 100644 appveyor.tmpl.yml create mode 100644 appveyor.yml diff --git a/.travis.yml b/.travis.yml index 45f8875..7d91fbc 100644 --- a/.travis.yml +++ b/.travis.yml @@ -1,41 +1,37 @@ -language: python -python: 2.7 -env: - global: - LD_PRELOAD=/lib/x86_64-linux-gnu/libSegFault.so - matrix: - - TOXENV=check - - TOXENV=py2-2.6,coveralls - - TOXENV=py2-2.6-ASPECTLIB_DEBUG=yes,coveralls - - TOXENV=py2-2.6-nocover - - TOXENV=py2-2.6-nocover-ASPECTLIB_DEBUG=yes - - TOXENV=py2-2.7,coveralls - - TOXENV=py2-2.7-ASPECTLIB_DEBUG=yes,coveralls - - TOXENV=py2-2.7-nocover - - TOXENV=py2-2.7-nocover-ASPECTLIB_DEBUG=yes - - TOXENV=py2-pypy,coveralls - - TOXENV=py2-pypy-ASPECTLIB_DEBUG=yes,coveralls - - TOXENV=py2-pypy-nocover - - TOXENV=py2-pypy-nocover-ASPECTLIB_DEBUG=yes - - TOXENV=py3-3.3,coveralls - - TOXENV=py3-3.3-ASPECTLIB_DEBUG=yes,coveralls - - TOXENV=py3-3.3-nocover - - TOXENV=py3-3.3-nocover-ASPECTLIB_DEBUG=yes - - TOXENV=py3-3.4,coveralls - - TOXENV=py3-3.4-ASPECTLIB_DEBUG=yes,coveralls - - TOXENV=py3-3.4-nocover - - TOXENV=py3-3.4-nocover-ASPECTLIB_DEBUG=yes -before_install: - - python --version - - virtualenv --version - - pip --version - - uname -a - - lsb_release -a -install: - - pip install tox -script: - - tox -v -notifications: - email: - on_success: never - on_failure: always +language: python +python: 2.7 +env: + global: + LD_PRELOAD=/lib/x86_64-linux-gnu/libSegFault.so + matrix: + - TOXENV=check + - TOXENV=2.6,coveralls + - TOXENV=2.6-ASPECTLIB_DEBUG=yes,coveralls + - TOXENV=2.6-nocover + - TOXENV=2.6-nocover-ASPECTLIB_DEBUG=yes + - TOXENV=2.7,coveralls + - TOXENV=2.7-ASPECTLIB_DEBUG=yes,coveralls + - TOXENV=2.7-nocover + - TOXENV=2.7-nocover-ASPECTLIB_DEBUG=yes + - TOXENV=asyncio-3.3,coveralls + - TOXENV=asyncio-3.3-ASPECTLIB_DEBUG=yes,coveralls + - TOXENV=asyncio-3.3-nocover + - TOXENV=asyncio-3.3-nocover-ASPECTLIB_DEBUG=yes + - TOXENV=pypy,coveralls + - TOXENV=pypy-ASPECTLIB_DEBUG=yes,coveralls + - TOXENV=pypy-nocover + - TOXENV=pypy-nocover-ASPECTLIB_DEBUG=yes +before_install: + - python --version + - virtualenv --version + - pip --version + - uname -a + - lsb_release -a +install: + - pip install tox +script: + - tox -v +notifications: + email: + on_success: never + on_failure: always diff --git a/appveyor.tmpl.yml b/appveyor.tmpl.yml new file mode 100644 index 0000000..42d07c5 --- /dev/null +++ b/appveyor.tmpl.yml @@ -0,0 +1,18 @@ +build: false +environment: + matrix: + - TOXENV=check +{% for env, config in tox_environments|dictsort %} + - TOXENV={{ env }}{% if config.cover %},coveralls{% endif %} + +{% endfor %} +init: + - "ECHO %TOXENV%" + - ps: "ls C:/Python*" +install: + - ps: (new-object net.webclient).DownloadFile('https://bootstrap.pypa.io/get-pip.py') + - "c:/python27/python.exe C:/get-pip.py tox virtualenv" +test_script: + - "c:/python27/Scripts/tox.exe --version" + - "c:/python27/Scripts/virtualenv --version" + - "c:/python27/Scripts/pip --version" diff --git a/appveyor.yml b/appveyor.yml new file mode 100644 index 0000000..c6df7a9 --- /dev/null +++ b/appveyor.yml @@ -0,0 +1,30 @@ +build: false +environment: + matrix: + - TOXENV=check + - TOXENV=2.6,coveralls + - TOXENV=2.6-ASPECTLIB_DEBUG=yes,coveralls + - TOXENV=2.6-nocover + - TOXENV=2.6-nocover-ASPECTLIB_DEBUG=yes + - TOXENV=2.7,coveralls + - TOXENV=2.7-ASPECTLIB_DEBUG=yes,coveralls + - TOXENV=2.7-nocover + - TOXENV=2.7-nocover-ASPECTLIB_DEBUG=yes + - TOXENV=asyncio-3.3,coveralls + - TOXENV=asyncio-3.3-ASPECTLIB_DEBUG=yes,coveralls + - TOXENV=asyncio-3.3-nocover + - TOXENV=asyncio-3.3-nocover-ASPECTLIB_DEBUG=yes + - TOXENV=pypy,coveralls + - TOXENV=pypy-ASPECTLIB_DEBUG=yes,coveralls + - TOXENV=pypy-nocover + - TOXENV=pypy-nocover-ASPECTLIB_DEBUG=yes +init: + - "ECHO %TOXENV%" + - ps: "ls C:/Python*" +install: + - ps: (new-object net.webclient).DownloadFile('https://bootstrap.pypa.io/get-pip.py') + - "c:/python27/python.exe C:/get-pip.py tox virtualenv" +test_script: + - "c:/python27/Scripts/tox.exe --version" + - "c:/python27/Scripts/virtualenv --version" + - "c:/python27/Scripts/pip --version" diff --git a/configure b/configure index dc77f3b..22c1742 100755 --- a/configure +++ b/configure @@ -31,10 +31,8 @@ for alias, conf in matrix.from_file('setup.cfg').items(): 'env_vars': env_vars.split(), } -with open('tox.ini', 'w') as fh: - fh.write(jinja.get_template('tox.tmpl.ini').render(tox_environments=tox_environments)) - -with open('.travis.yml', 'w') as fh: - fh.write(jinja.get_template('.travis.tmpl.yml').render(tox_environments=tox_environments)) +for name in ('tox.tmpl.ini', '.travis.tmpl.yml', 'appveyor.tmpl.yml'): + with open(name.replace('.tmpl', ''), 'w') as fh: + fh.write(jinja.get_template(name).render(tox_environments=tox_environments)) print("DONE.") diff --git a/setup.cfg b/setup.cfg index 45d2384..370179a 100644 --- a/setup.cfg +++ b/setup.cfg @@ -23,8 +23,8 @@ addopts = [matrix] dependencies = - py2: trollius MySQL-python !python_versions[3.*] - py3: asyncio &python_versions[3.*] + : trollius MySQL-python !python_versions[3.*] + asyncio &python_versions[3.3] python_versions = 2.6 diff --git a/tox.ini b/tox.ini index e40e567..954c66f 100644 --- a/tox.ini +++ b/tox.ini @@ -1,358 +1,306 @@ -[tox] -envlist = - clean, - check, - py2-2.6, - py2-2.6-ASPECTLIB_DEBUG=yes, - py2-2.6-nocover, - py2-2.6-nocover-ASPECTLIB_DEBUG=yes, - py2-2.7, - py2-2.7-ASPECTLIB_DEBUG=yes, - py2-2.7-nocover, - py2-2.7-nocover-ASPECTLIB_DEBUG=yes, - py2-pypy, - py2-pypy-ASPECTLIB_DEBUG=yes, - py2-pypy-nocover, - py2-pypy-nocover-ASPECTLIB_DEBUG=yes, - py3-3.3, - py3-3.3-ASPECTLIB_DEBUG=yes, - py3-3.3-nocover, - py3-3.3-nocover-ASPECTLIB_DEBUG=yes, - py3-3.4, - py3-3.4-ASPECTLIB_DEBUG=yes, - py3-3.4-nocover, - py3-3.4-nocover-ASPECTLIB_DEBUG=yes, - report, - docs - -[testenv] -setenv = - PYTHONPATH={toxinidir}/tests - PYTHONUNBUFFERED=yes -deps = - pytest - pytest-greendots - pytest-capturelog - process-tests - nose - mock - tornado -commands = - {posargs:py.test -vv} - -[testenv:spell] -setenv = - SPELLCHECK = 1 -commands = - sphinx-build -b spelling docs dist/docs -usedevelop = true -deps = - -r{toxinidir}/docs/requirements.txt - sphinxcontrib-spelling - pyenchant - -[testenv:docs] -whitelist_externals = - rm -commands = - rm -rf dist/docs - sphinx-build -b html docs dist/docs - sphinx-build -b linkcheck docs dist/docs -usedevelop = true -deps = - -r{toxinidir}/docs/requirements.txt - -[testenv:configure] -deps = - jinja2 - matrix -usedevelop = true -commands = - python configure - -[testenv:check] -basepython = python3.4 -deps = - docutils - check-manifest - flake8 -usedevelop = true -commands = - python setup.py check --restructuredtext --strict --metadata - check-manifest {toxinidir} - flake8 src - -[testenv:coveralls] -deps = - coveralls -usedevelop = true -commands = - coverage combine - coverage report - coveralls - -[testenv:report] -basepython = python3.4 -commands = - coverage combine - coverage report -usedevelop = true -deps = coverage - -[testenv:clean] -commands = coverage erase -usedevelop = true -deps = coverage - -[testenv:py2-2.6] -basepython = python2.6 -setenv = - {[testenv]setenv} - WITH_COVERAGE=yes -usedevelop = true -commands = - {posargs:py.test --cov src -vv} -deps = - {[testenv]deps} - coverage - pytest-cov - trollius - MySQL-python - -[testenv:py2-2.6-ASPECTLIB_DEBUG=yes] -basepython = python2.6 -setenv = - {[testenv]setenv} - ASPECTLIB_DEBUG=yes - WITH_COVERAGE=yes -usedevelop = true -commands = - {posargs:py.test --cov src -vv} -deps = - {[testenv]deps} - coverage - pytest-cov - trollius - MySQL-python - -[testenv:py2-2.6-nocover] -basepython = python2.6 -deps = - {[testenv]deps} - coverage - pytest-cov - trollius - MySQL-python - -[testenv:py2-2.6-nocover-ASPECTLIB_DEBUG=yes] -basepython = python2.6 -setenv = - {[testenv]setenv} - ASPECTLIB_DEBUG=yes -deps = - {[testenv]deps} - coverage - pytest-cov - trollius - MySQL-python - -[testenv:py2-2.7] -basepython = python2.7 -setenv = - {[testenv]setenv} - WITH_COVERAGE=yes -usedevelop = true -commands = - {posargs:py.test --cov src -vv} -deps = - {[testenv]deps} - coverage - pytest-cov - trollius - MySQL-python - -[testenv:py2-2.7-ASPECTLIB_DEBUG=yes] -basepython = python2.7 -setenv = - {[testenv]setenv} - ASPECTLIB_DEBUG=yes - WITH_COVERAGE=yes -usedevelop = true -commands = - {posargs:py.test --cov src -vv} -deps = - {[testenv]deps} - coverage - pytest-cov - trollius - MySQL-python - -[testenv:py2-2.7-nocover] -basepython = python2.7 -deps = - {[testenv]deps} - coverage - pytest-cov - trollius - MySQL-python - -[testenv:py2-2.7-nocover-ASPECTLIB_DEBUG=yes] -basepython = python2.7 -setenv = - {[testenv]setenv} - ASPECTLIB_DEBUG=yes -deps = - {[testenv]deps} - coverage - pytest-cov - trollius - MySQL-python - -[testenv:py2-pypy] -basepython = pypy -setenv = - {[testenv]setenv} - WITH_COVERAGE=yes -usedevelop = true -commands = - {posargs:py.test --cov src -vv} -deps = - {[testenv]deps} - coverage - pytest-cov - trollius - MySQL-python - -[testenv:py2-pypy-ASPECTLIB_DEBUG=yes] -basepython = pypy -setenv = - {[testenv]setenv} - ASPECTLIB_DEBUG=yes - WITH_COVERAGE=yes -usedevelop = true -commands = - {posargs:py.test --cov src -vv} -deps = - {[testenv]deps} - coverage - pytest-cov - trollius - MySQL-python - -[testenv:py2-pypy-nocover] -basepython = pypy -deps = - {[testenv]deps} - coverage - pytest-cov - trollius - MySQL-python - -[testenv:py2-pypy-nocover-ASPECTLIB_DEBUG=yes] -basepython = pypy -setenv = - {[testenv]setenv} - ASPECTLIB_DEBUG=yes -deps = - {[testenv]deps} - coverage - pytest-cov - trollius - MySQL-python - -[testenv:py3-3.3] -basepython = python3.3 -setenv = - {[testenv]setenv} - WITH_COVERAGE=yes -usedevelop = true -commands = - {posargs:py.test --cov src -vv} -deps = - {[testenv]deps} - coverage - pytest-cov - asyncio - -[testenv:py3-3.3-ASPECTLIB_DEBUG=yes] -basepython = python3.3 -setenv = - {[testenv]setenv} - ASPECTLIB_DEBUG=yes - WITH_COVERAGE=yes -usedevelop = true -commands = - {posargs:py.test --cov src -vv} -deps = - {[testenv]deps} - coverage - pytest-cov - asyncio - -[testenv:py3-3.3-nocover] -basepython = python3.3 -deps = - {[testenv]deps} - coverage - pytest-cov - asyncio - -[testenv:py3-3.3-nocover-ASPECTLIB_DEBUG=yes] -basepython = python3.3 -setenv = - {[testenv]setenv} - ASPECTLIB_DEBUG=yes -deps = - {[testenv]deps} - coverage - pytest-cov - asyncio - -[testenv:py3-3.4] -basepython = python3.4 -setenv = - {[testenv]setenv} - WITH_COVERAGE=yes -usedevelop = true -commands = - {posargs:py.test --cov src -vv} -deps = - {[testenv]deps} - coverage - pytest-cov - asyncio - -[testenv:py3-3.4-ASPECTLIB_DEBUG=yes] -basepython = python3.4 -setenv = - {[testenv]setenv} - ASPECTLIB_DEBUG=yes - WITH_COVERAGE=yes -usedevelop = true -commands = - {posargs:py.test --cov src -vv} -deps = - {[testenv]deps} - coverage - pytest-cov - asyncio - -[testenv:py3-3.4-nocover] -basepython = python3.4 -deps = - {[testenv]deps} - coverage - pytest-cov - asyncio - -[testenv:py3-3.4-nocover-ASPECTLIB_DEBUG=yes] -basepython = python3.4 -setenv = - {[testenv]setenv} - ASPECTLIB_DEBUG=yes -deps = - {[testenv]deps} - coverage - pytest-cov - asyncio - +[tox] +envlist = + clean, + check, + 2.6, + 2.6-ASPECTLIB_DEBUG=yes, + 2.6-nocover, + 2.6-nocover-ASPECTLIB_DEBUG=yes, + 2.7, + 2.7-ASPECTLIB_DEBUG=yes, + 2.7-nocover, + 2.7-nocover-ASPECTLIB_DEBUG=yes, + asyncio-3.3, + asyncio-3.3-ASPECTLIB_DEBUG=yes, + asyncio-3.3-nocover, + asyncio-3.3-nocover-ASPECTLIB_DEBUG=yes, + pypy, + pypy-ASPECTLIB_DEBUG=yes, + pypy-nocover, + pypy-nocover-ASPECTLIB_DEBUG=yes, + report, + docs + +[testenv] +setenv = + PYTHONPATH={toxinidir}/tests + PYTHONUNBUFFERED=yes +deps = + pytest + pytest-greendots + pytest-capturelog + process-tests + nose + mock + tornado +commands = + {posargs:py.test -vv} + +[testenv:spell] +setenv = + SPELLCHECK = 1 +commands = + sphinx-build -b spelling docs dist/docs +usedevelop = true +deps = + -r{toxinidir}/docs/requirements.txt + sphinxcontrib-spelling + pyenchant + +[testenv:docs] +whitelist_externals = + rm +commands = + rm -rf dist/docs + sphinx-build -b html docs dist/docs + sphinx-build -b linkcheck docs dist/docs +usedevelop = true +deps = + -r{toxinidir}/docs/requirements.txt + +[testenv:configure] +deps = + jinja2 + matrix +usedevelop = true +commands = + python configure + +[testenv:check] +basepython = python3.4 +deps = + docutils + check-manifest + flake8 +usedevelop = true +commands = + python setup.py check --restructuredtext --strict --metadata + check-manifest {toxinidir} + flake8 src + +[testenv:coveralls] +deps = + coveralls +usedevelop = true +commands = + coverage combine + coverage report + coveralls + +[testenv:report] +basepython = python3.4 +commands = + coverage combine + coverage report +usedevelop = true +deps = coverage + +[testenv:clean] +commands = coverage erase +usedevelop = true +deps = coverage + +[testenv:2.6] +basepython = python2.6 +setenv = + {[testenv]setenv} + WITH_COVERAGE=yes +usedevelop = true +commands = + {posargs:py.test --cov src -vv} +deps = + {[testenv]deps} + coverage + pytest-cov + trollius + MySQL-python + +[testenv:2.6-ASPECTLIB_DEBUG=yes] +basepython = python2.6 +setenv = + {[testenv]setenv} + ASPECTLIB_DEBUG=yes + WITH_COVERAGE=yes +usedevelop = true +commands = + {posargs:py.test --cov src -vv} +deps = + {[testenv]deps} + coverage + pytest-cov + trollius + MySQL-python + +[testenv:2.6-nocover] +basepython = python2.6 +deps = + {[testenv]deps} + coverage + pytest-cov + trollius + MySQL-python + +[testenv:2.6-nocover-ASPECTLIB_DEBUG=yes] +basepython = python2.6 +setenv = + {[testenv]setenv} + ASPECTLIB_DEBUG=yes +deps = + {[testenv]deps} + coverage + pytest-cov + trollius + MySQL-python + +[testenv:2.7] +basepython = python2.7 +setenv = + {[testenv]setenv} + WITH_COVERAGE=yes +usedevelop = true +commands = + {posargs:py.test --cov src -vv} +deps = + {[testenv]deps} + coverage + pytest-cov + trollius + MySQL-python + +[testenv:2.7-ASPECTLIB_DEBUG=yes] +basepython = python2.7 +setenv = + {[testenv]setenv} + ASPECTLIB_DEBUG=yes + WITH_COVERAGE=yes +usedevelop = true +commands = + {posargs:py.test --cov src -vv} +deps = + {[testenv]deps} + coverage + pytest-cov + trollius + MySQL-python + +[testenv:2.7-nocover] +basepython = python2.7 +deps = + {[testenv]deps} + coverage + pytest-cov + trollius + MySQL-python + +[testenv:2.7-nocover-ASPECTLIB_DEBUG=yes] +basepython = python2.7 +setenv = + {[testenv]setenv} + ASPECTLIB_DEBUG=yes +deps = + {[testenv]deps} + coverage + pytest-cov + trollius + MySQL-python + +[testenv:asyncio-3.3] +basepython = python3.3 +setenv = + {[testenv]setenv} + WITH_COVERAGE=yes +usedevelop = true +commands = + {posargs:py.test --cov src -vv} +deps = + {[testenv]deps} + coverage + pytest-cov + asyncio + +[testenv:asyncio-3.3-ASPECTLIB_DEBUG=yes] +basepython = python3.3 +setenv = + {[testenv]setenv} + ASPECTLIB_DEBUG=yes + WITH_COVERAGE=yes +usedevelop = true +commands = + {posargs:py.test --cov src -vv} +deps = + {[testenv]deps} + coverage + pytest-cov + asyncio + +[testenv:asyncio-3.3-nocover] +basepython = python3.3 +deps = + {[testenv]deps} + coverage + pytest-cov + asyncio + +[testenv:asyncio-3.3-nocover-ASPECTLIB_DEBUG=yes] +basepython = python3.3 +setenv = + {[testenv]setenv} + ASPECTLIB_DEBUG=yes +deps = + {[testenv]deps} + coverage + pytest-cov + asyncio + +[testenv:pypy] +basepython = pypy +setenv = + {[testenv]setenv} + WITH_COVERAGE=yes +usedevelop = true +commands = + {posargs:py.test --cov src -vv} +deps = + {[testenv]deps} + coverage + pytest-cov + trollius + MySQL-python + +[testenv:pypy-ASPECTLIB_DEBUG=yes] +basepython = pypy +setenv = + {[testenv]setenv} + ASPECTLIB_DEBUG=yes + WITH_COVERAGE=yes +usedevelop = true +commands = + {posargs:py.test --cov src -vv} +deps = + {[testenv]deps} + coverage + pytest-cov + trollius + MySQL-python + +[testenv:pypy-nocover] +basepython = pypy +deps = + {[testenv]deps} + coverage + pytest-cov + trollius + MySQL-python + +[testenv:pypy-nocover-ASPECTLIB_DEBUG=yes] +basepython = pypy +setenv = + {[testenv]setenv} + ASPECTLIB_DEBUG=yes +deps = + {[testenv]deps} + coverage + pytest-cov + trollius + MySQL-python + From c0e8bbd8763f4eac1d4fbafa5c53e1c07552875e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ionel=20Cristian=20M=C4=83rie=C8=99?= Date: Sun, 15 Jun 2014 05:39:55 +0300 Subject: [PATCH 002/179] Ooops --- appveyor.tmpl.yml | 4 ++-- appveyor.yml | 50 +++++++++++++++++++++++++++++++---------------- 2 files changed, 35 insertions(+), 19 deletions(-) diff --git a/appveyor.tmpl.yml b/appveyor.tmpl.yml index 42d07c5..2d92bad 100644 --- a/appveyor.tmpl.yml +++ b/appveyor.tmpl.yml @@ -1,9 +1,9 @@ build: false environment: matrix: - - TOXENV=check + - TOXENV: check {% for env, config in tox_environments|dictsort %} - - TOXENV={{ env }}{% if config.cover %},coveralls{% endif %} + - TOXENV: "{{ env }}{% if config.cover %},coveralls{% endif %}" {% endfor %} init: diff --git a/appveyor.yml b/appveyor.yml index c6df7a9..0011f72 100644 --- a/appveyor.yml +++ b/appveyor.yml @@ -1,23 +1,39 @@ build: false environment: matrix: - - TOXENV=check - - TOXENV=2.6,coveralls - - TOXENV=2.6-ASPECTLIB_DEBUG=yes,coveralls - - TOXENV=2.6-nocover - - TOXENV=2.6-nocover-ASPECTLIB_DEBUG=yes - - TOXENV=2.7,coveralls - - TOXENV=2.7-ASPECTLIB_DEBUG=yes,coveralls - - TOXENV=2.7-nocover - - TOXENV=2.7-nocover-ASPECTLIB_DEBUG=yes - - TOXENV=asyncio-3.3,coveralls - - TOXENV=asyncio-3.3-ASPECTLIB_DEBUG=yes,coveralls - - TOXENV=asyncio-3.3-nocover - - TOXENV=asyncio-3.3-nocover-ASPECTLIB_DEBUG=yes - - TOXENV=pypy,coveralls - - TOXENV=pypy-ASPECTLIB_DEBUG=yes,coveralls - - TOXENV=pypy-nocover - - TOXENV=pypy-nocover-ASPECTLIB_DEBUG=yes + - TOXENV: check + - TOXENV: "2.6,coveralls" + + - TOXENV: "2.6-ASPECTLIB_DEBUG=yes,coveralls" + + - TOXENV: "2.6-nocover" + + - TOXENV: "2.6-nocover-ASPECTLIB_DEBUG=yes" + + - TOXENV: "2.7,coveralls" + + - TOXENV: "2.7-ASPECTLIB_DEBUG=yes,coveralls" + + - TOXENV: "2.7-nocover" + + - TOXENV: "2.7-nocover-ASPECTLIB_DEBUG=yes" + + - TOXENV: "asyncio-3.3,coveralls" + + - TOXENV: "asyncio-3.3-ASPECTLIB_DEBUG=yes,coveralls" + + - TOXENV: "asyncio-3.3-nocover" + + - TOXENV: "asyncio-3.3-nocover-ASPECTLIB_DEBUG=yes" + + - TOXENV: "pypy,coveralls" + + - TOXENV: "pypy-ASPECTLIB_DEBUG=yes,coveralls" + + - TOXENV: "pypy-nocover" + + - TOXENV: "pypy-nocover-ASPECTLIB_DEBUG=yes" + init: - "ECHO %TOXENV%" - ps: "ls C:/Python*" From 64fa93da94dc3ab4eef4e9d51a5610d02b9217d2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ionel=20Cristian=20M=C4=83rie=C8=99?= Date: Sun, 15 Jun 2014 05:48:15 +0300 Subject: [PATCH 003/179] Remove empty lines. --- appveyor.tmpl.yml | 1 - appveyor.yml | 16 ---------------- 2 files changed, 17 deletions(-) diff --git a/appveyor.tmpl.yml b/appveyor.tmpl.yml index 2d92bad..998972f 100644 --- a/appveyor.tmpl.yml +++ b/appveyor.tmpl.yml @@ -4,7 +4,6 @@ environment: - TOXENV: check {% for env, config in tox_environments|dictsort %} - TOXENV: "{{ env }}{% if config.cover %},coveralls{% endif %}" - {% endfor %} init: - "ECHO %TOXENV%" diff --git a/appveyor.yml b/appveyor.yml index 0011f72..df91fde 100644 --- a/appveyor.yml +++ b/appveyor.yml @@ -3,37 +3,21 @@ environment: matrix: - TOXENV: check - TOXENV: "2.6,coveralls" - - TOXENV: "2.6-ASPECTLIB_DEBUG=yes,coveralls" - - TOXENV: "2.6-nocover" - - TOXENV: "2.6-nocover-ASPECTLIB_DEBUG=yes" - - TOXENV: "2.7,coveralls" - - TOXENV: "2.7-ASPECTLIB_DEBUG=yes,coveralls" - - TOXENV: "2.7-nocover" - - TOXENV: "2.7-nocover-ASPECTLIB_DEBUG=yes" - - TOXENV: "asyncio-3.3,coveralls" - - TOXENV: "asyncio-3.3-ASPECTLIB_DEBUG=yes,coveralls" - - TOXENV: "asyncio-3.3-nocover" - - TOXENV: "asyncio-3.3-nocover-ASPECTLIB_DEBUG=yes" - - TOXENV: "pypy,coveralls" - - TOXENV: "pypy-ASPECTLIB_DEBUG=yes,coveralls" - - TOXENV: "pypy-nocover" - - TOXENV: "pypy-nocover-ASPECTLIB_DEBUG=yes" - init: - "ECHO %TOXENV%" - ps: "ls C:/Python*" From b2f996a846d4e250c503f075ac0afa98899b308e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ionel=20Cristian=20M=C4=83rie=C8=99?= Date: Sun, 15 Jun 2014 05:53:31 +0300 Subject: [PATCH 004/179] Only run 2.7 and 3.4 on appveyor. Add back 3.4 (oops). --- .travis.yml | 36 +++++++------- appveyor.tmpl.yml | 4 +- appveyor.yml | 24 ++++------ setup.cfg | 9 ++-- tox.ini | 116 +++++++++++++++++++++++++++++++++------------- 5 files changed, 119 insertions(+), 70 deletions(-) diff --git a/.travis.yml b/.travis.yml index 7d91fbc..9fb8059 100644 --- a/.travis.yml +++ b/.travis.yml @@ -5,22 +5,26 @@ env: LD_PRELOAD=/lib/x86_64-linux-gnu/libSegFault.so matrix: - TOXENV=check - - TOXENV=2.6,coveralls - - TOXENV=2.6-ASPECTLIB_DEBUG=yes,coveralls - - TOXENV=2.6-nocover - - TOXENV=2.6-nocover-ASPECTLIB_DEBUG=yes - - TOXENV=2.7,coveralls - - TOXENV=2.7-ASPECTLIB_DEBUG=yes,coveralls - - TOXENV=2.7-nocover - - TOXENV=2.7-nocover-ASPECTLIB_DEBUG=yes - - TOXENV=asyncio-3.3,coveralls - - TOXENV=asyncio-3.3-ASPECTLIB_DEBUG=yes,coveralls - - TOXENV=asyncio-3.3-nocover - - TOXENV=asyncio-3.3-nocover-ASPECTLIB_DEBUG=yes - - TOXENV=pypy,coveralls - - TOXENV=pypy-ASPECTLIB_DEBUG=yes,coveralls - - TOXENV=pypy-nocover - - TOXENV=pypy-nocover-ASPECTLIB_DEBUG=yes + - TOXENV=2.6-trollius-mysql,coveralls + - TOXENV=2.6-trollius-mysql-ASPECTLIB_DEBUG=yes,coveralls + - TOXENV=2.6-trollius-mysql-nocover + - TOXENV=2.6-trollius-mysql-nocover-ASPECTLIB_DEBUG=yes + - TOXENV=2.7-trollius-mysql,coveralls + - TOXENV=2.7-trollius-mysql-ASPECTLIB_DEBUG=yes,coveralls + - TOXENV=2.7-trollius-mysql-nocover + - TOXENV=2.7-trollius-mysql-nocover-ASPECTLIB_DEBUG=yes + - TOXENV=3.3-asyncio,coveralls + - TOXENV=3.3-asyncio-ASPECTLIB_DEBUG=yes,coveralls + - TOXENV=3.3-asyncio-nocover + - TOXENV=3.3-asyncio-nocover-ASPECTLIB_DEBUG=yes + - TOXENV=3.4,coveralls + - TOXENV=3.4-ASPECTLIB_DEBUG=yes,coveralls + - TOXENV=3.4-nocover + - TOXENV=3.4-nocover-ASPECTLIB_DEBUG=yes + - TOXENV=pypy-trollius-mysql,coveralls + - TOXENV=pypy-trollius-mysql-ASPECTLIB_DEBUG=yes,coveralls + - TOXENV=pypy-trollius-mysql-nocover + - TOXENV=pypy-trollius-mysql-nocover-ASPECTLIB_DEBUG=yes before_install: - python --version - virtualenv --version diff --git a/appveyor.tmpl.yml b/appveyor.tmpl.yml index 998972f..1c90748 100644 --- a/appveyor.tmpl.yml +++ b/appveyor.tmpl.yml @@ -2,9 +2,9 @@ build: false environment: matrix: - TOXENV: check -{% for env, config in tox_environments|dictsort %} +{% for env, config in tox_environments|dictsort %}{% if '2.7' in env or '3.4' in env %} - TOXENV: "{{ env }}{% if config.cover %},coveralls{% endif %}" -{% endfor %} +{% endif %}{% endfor %} init: - "ECHO %TOXENV%" - ps: "ls C:/Python*" diff --git a/appveyor.yml b/appveyor.yml index df91fde..208c917 100644 --- a/appveyor.yml +++ b/appveyor.yml @@ -2,22 +2,14 @@ build: false environment: matrix: - TOXENV: check - - TOXENV: "2.6,coveralls" - - TOXENV: "2.6-ASPECTLIB_DEBUG=yes,coveralls" - - TOXENV: "2.6-nocover" - - TOXENV: "2.6-nocover-ASPECTLIB_DEBUG=yes" - - TOXENV: "2.7,coveralls" - - TOXENV: "2.7-ASPECTLIB_DEBUG=yes,coveralls" - - TOXENV: "2.7-nocover" - - TOXENV: "2.7-nocover-ASPECTLIB_DEBUG=yes" - - TOXENV: "asyncio-3.3,coveralls" - - TOXENV: "asyncio-3.3-ASPECTLIB_DEBUG=yes,coveralls" - - TOXENV: "asyncio-3.3-nocover" - - TOXENV: "asyncio-3.3-nocover-ASPECTLIB_DEBUG=yes" - - TOXENV: "pypy,coveralls" - - TOXENV: "pypy-ASPECTLIB_DEBUG=yes,coveralls" - - TOXENV: "pypy-nocover" - - TOXENV: "pypy-nocover-ASPECTLIB_DEBUG=yes" + - TOXENV: "2.7-trollius-mysql,coveralls" + - TOXENV: "2.7-trollius-mysql-ASPECTLIB_DEBUG=yes,coveralls" + - TOXENV: "2.7-trollius-mysql-nocover" + - TOXENV: "2.7-trollius-mysql-nocover-ASPECTLIB_DEBUG=yes" + - TOXENV: "3.4,coveralls" + - TOXENV: "3.4-ASPECTLIB_DEBUG=yes,coveralls" + - TOXENV: "3.4-nocover" + - TOXENV: "3.4-nocover-ASPECTLIB_DEBUG=yes" init: - "ECHO %TOXENV%" - ps: "ls C:/Python*" diff --git a/setup.cfg b/setup.cfg index 370179a..543a869 100644 --- a/setup.cfg +++ b/setup.cfg @@ -22,10 +22,6 @@ addopts = --cov-report term-missing [matrix] -dependencies = - : trollius MySQL-python !python_versions[3.*] - asyncio &python_versions[3.3] - python_versions = 2.6 2.7 @@ -33,6 +29,11 @@ python_versions = 3.4 pypy +dependencies = + trollius-mysql: trollius MySQL-python !python_versions[3.*] + asyncio: asyncio &python_versions[3.3] + :- &python_versions[3.4] + coverage_flags = : true nocover: false diff --git a/tox.ini b/tox.ini index 954c66f..cf8c025 100644 --- a/tox.ini +++ b/tox.ini @@ -2,22 +2,26 @@ envlist = clean, check, - 2.6, - 2.6-ASPECTLIB_DEBUG=yes, - 2.6-nocover, - 2.6-nocover-ASPECTLIB_DEBUG=yes, - 2.7, - 2.7-ASPECTLIB_DEBUG=yes, - 2.7-nocover, - 2.7-nocover-ASPECTLIB_DEBUG=yes, - asyncio-3.3, - asyncio-3.3-ASPECTLIB_DEBUG=yes, - asyncio-3.3-nocover, - asyncio-3.3-nocover-ASPECTLIB_DEBUG=yes, - pypy, - pypy-ASPECTLIB_DEBUG=yes, - pypy-nocover, - pypy-nocover-ASPECTLIB_DEBUG=yes, + 2.6-trollius-mysql, + 2.6-trollius-mysql-ASPECTLIB_DEBUG=yes, + 2.6-trollius-mysql-nocover, + 2.6-trollius-mysql-nocover-ASPECTLIB_DEBUG=yes, + 2.7-trollius-mysql, + 2.7-trollius-mysql-ASPECTLIB_DEBUG=yes, + 2.7-trollius-mysql-nocover, + 2.7-trollius-mysql-nocover-ASPECTLIB_DEBUG=yes, + 3.3-asyncio, + 3.3-asyncio-ASPECTLIB_DEBUG=yes, + 3.3-asyncio-nocover, + 3.3-asyncio-nocover-ASPECTLIB_DEBUG=yes, + 3.4, + 3.4-ASPECTLIB_DEBUG=yes, + 3.4-nocover, + 3.4-nocover-ASPECTLIB_DEBUG=yes, + pypy-trollius-mysql, + pypy-trollius-mysql-ASPECTLIB_DEBUG=yes, + pypy-trollius-mysql-nocover, + pypy-trollius-mysql-nocover-ASPECTLIB_DEBUG=yes, report, docs @@ -100,7 +104,7 @@ commands = coverage erase usedevelop = true deps = coverage -[testenv:2.6] +[testenv:2.6-trollius-mysql] basepython = python2.6 setenv = {[testenv]setenv} @@ -115,7 +119,7 @@ deps = trollius MySQL-python -[testenv:2.6-ASPECTLIB_DEBUG=yes] +[testenv:2.6-trollius-mysql-ASPECTLIB_DEBUG=yes] basepython = python2.6 setenv = {[testenv]setenv} @@ -131,7 +135,7 @@ deps = trollius MySQL-python -[testenv:2.6-nocover] +[testenv:2.6-trollius-mysql-nocover] basepython = python2.6 deps = {[testenv]deps} @@ -140,7 +144,7 @@ deps = trollius MySQL-python -[testenv:2.6-nocover-ASPECTLIB_DEBUG=yes] +[testenv:2.6-trollius-mysql-nocover-ASPECTLIB_DEBUG=yes] basepython = python2.6 setenv = {[testenv]setenv} @@ -152,7 +156,7 @@ deps = trollius MySQL-python -[testenv:2.7] +[testenv:2.7-trollius-mysql] basepython = python2.7 setenv = {[testenv]setenv} @@ -167,7 +171,7 @@ deps = trollius MySQL-python -[testenv:2.7-ASPECTLIB_DEBUG=yes] +[testenv:2.7-trollius-mysql-ASPECTLIB_DEBUG=yes] basepython = python2.7 setenv = {[testenv]setenv} @@ -183,7 +187,7 @@ deps = trollius MySQL-python -[testenv:2.7-nocover] +[testenv:2.7-trollius-mysql-nocover] basepython = python2.7 deps = {[testenv]deps} @@ -192,7 +196,7 @@ deps = trollius MySQL-python -[testenv:2.7-nocover-ASPECTLIB_DEBUG=yes] +[testenv:2.7-trollius-mysql-nocover-ASPECTLIB_DEBUG=yes] basepython = python2.7 setenv = {[testenv]setenv} @@ -204,7 +208,7 @@ deps = trollius MySQL-python -[testenv:asyncio-3.3] +[testenv:3.3-asyncio] basepython = python3.3 setenv = {[testenv]setenv} @@ -218,7 +222,7 @@ deps = pytest-cov asyncio -[testenv:asyncio-3.3-ASPECTLIB_DEBUG=yes] +[testenv:3.3-asyncio-ASPECTLIB_DEBUG=yes] basepython = python3.3 setenv = {[testenv]setenv} @@ -233,7 +237,7 @@ deps = pytest-cov asyncio -[testenv:asyncio-3.3-nocover] +[testenv:3.3-asyncio-nocover] basepython = python3.3 deps = {[testenv]deps} @@ -241,7 +245,7 @@ deps = pytest-cov asyncio -[testenv:asyncio-3.3-nocover-ASPECTLIB_DEBUG=yes] +[testenv:3.3-asyncio-nocover-ASPECTLIB_DEBUG=yes] basepython = python3.3 setenv = {[testenv]setenv} @@ -252,7 +256,55 @@ deps = pytest-cov asyncio -[testenv:pypy] +[testenv:3.4] +basepython = python3.4 +setenv = + {[testenv]setenv} + WITH_COVERAGE=yes +usedevelop = true +commands = + {posargs:py.test --cov src -vv} +deps = + {[testenv]deps} + coverage + pytest-cov + - + +[testenv:3.4-ASPECTLIB_DEBUG=yes] +basepython = python3.4 +setenv = + {[testenv]setenv} + ASPECTLIB_DEBUG=yes + WITH_COVERAGE=yes +usedevelop = true +commands = + {posargs:py.test --cov src -vv} +deps = + {[testenv]deps} + coverage + pytest-cov + - + +[testenv:3.4-nocover] +basepython = python3.4 +deps = + {[testenv]deps} + coverage + pytest-cov + - + +[testenv:3.4-nocover-ASPECTLIB_DEBUG=yes] +basepython = python3.4 +setenv = + {[testenv]setenv} + ASPECTLIB_DEBUG=yes +deps = + {[testenv]deps} + coverage + pytest-cov + - + +[testenv:pypy-trollius-mysql] basepython = pypy setenv = {[testenv]setenv} @@ -267,7 +319,7 @@ deps = trollius MySQL-python -[testenv:pypy-ASPECTLIB_DEBUG=yes] +[testenv:pypy-trollius-mysql-ASPECTLIB_DEBUG=yes] basepython = pypy setenv = {[testenv]setenv} @@ -283,7 +335,7 @@ deps = trollius MySQL-python -[testenv:pypy-nocover] +[testenv:pypy-trollius-mysql-nocover] basepython = pypy deps = {[testenv]deps} @@ -292,7 +344,7 @@ deps = trollius MySQL-python -[testenv:pypy-nocover-ASPECTLIB_DEBUG=yes] +[testenv:pypy-trollius-mysql-nocover-ASPECTLIB_DEBUG=yes] basepython = pypy setenv = {[testenv]setenv} From 7ed2df9e91b9afd7df8777a7f6f82d57de65b5f0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ionel=20Cristian=20M=C4=83rie=C8=99?= Date: Sun, 15 Jun 2014 06:01:52 +0300 Subject: [PATCH 005/179] Blah, didn't see I needed the second argument ... --- appveyor.tmpl.yml | 2 +- appveyor.yml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/appveyor.tmpl.yml b/appveyor.tmpl.yml index 1c90748..911a54a 100644 --- a/appveyor.tmpl.yml +++ b/appveyor.tmpl.yml @@ -9,7 +9,7 @@ init: - "ECHO %TOXENV%" - ps: "ls C:/Python*" install: - - ps: (new-object net.webclient).DownloadFile('https://bootstrap.pypa.io/get-pip.py') + - ps: (new-object net.webclient).DownloadFile('https://bootstrap.pypa.io/get-pip.py', 'C:/get-pip.py') - "c:/python27/python.exe C:/get-pip.py tox virtualenv" test_script: - "c:/python27/Scripts/tox.exe --version" diff --git a/appveyor.yml b/appveyor.yml index 208c917..dc13a67 100644 --- a/appveyor.yml +++ b/appveyor.yml @@ -14,7 +14,7 @@ init: - "ECHO %TOXENV%" - ps: "ls C:/Python*" install: - - ps: (new-object net.webclient).DownloadFile('https://bootstrap.pypa.io/get-pip.py') + - ps: (new-object net.webclient).DownloadFile('https://bootstrap.pypa.io/get-pip.py', 'C:/get-pip.py') - "c:/python27/python.exe C:/get-pip.py tox virtualenv" test_script: - "c:/python27/Scripts/tox.exe --version" From f10b494bfc1c29fca723dcf9ef1a359331265ef3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ionel=20Cristian=20M=C4=83rie=C8=99?= Date: Sun, 15 Jun 2014 06:05:31 +0300 Subject: [PATCH 006/179] Disable wheels. --- appveyor.tmpl.yml | 2 +- appveyor.yml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/appveyor.tmpl.yml b/appveyor.tmpl.yml index 911a54a..767e00a 100644 --- a/appveyor.tmpl.yml +++ b/appveyor.tmpl.yml @@ -10,7 +10,7 @@ init: - ps: "ls C:/Python*" install: - ps: (new-object net.webclient).DownloadFile('https://bootstrap.pypa.io/get-pip.py', 'C:/get-pip.py') - - "c:/python27/python.exe C:/get-pip.py tox virtualenv" + - "c:/python27/python.exe C:/get-pip.py --no-use-wheel setuptools tox virtualenv" test_script: - "c:/python27/Scripts/tox.exe --version" - "c:/python27/Scripts/virtualenv --version" diff --git a/appveyor.yml b/appveyor.yml index dc13a67..fef9727 100644 --- a/appveyor.yml +++ b/appveyor.yml @@ -15,7 +15,7 @@ init: - ps: "ls C:/Python*" install: - ps: (new-object net.webclient).DownloadFile('https://bootstrap.pypa.io/get-pip.py', 'C:/get-pip.py') - - "c:/python27/python.exe C:/get-pip.py tox virtualenv" + - "c:/python27/python.exe C:/get-pip.py --no-use-wheel setuptools tox virtualenv" test_script: - "c:/python27/Scripts/tox.exe --version" - "c:/python27/Scripts/virtualenv --version" From a4faa1ff972edea3673dd91e7813fcecb50c888e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ionel=20Cristian=20M=C4=83rie=C8=99?= Date: Sun, 15 Jun 2014 06:09:27 +0300 Subject: [PATCH 007/179] This is silly. --- appveyor.tmpl.yml | 2 +- appveyor.yml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/appveyor.tmpl.yml b/appveyor.tmpl.yml index 767e00a..691598c 100644 --- a/appveyor.tmpl.yml +++ b/appveyor.tmpl.yml @@ -10,7 +10,7 @@ init: - ps: "ls C:/Python*" install: - ps: (new-object net.webclient).DownloadFile('https://bootstrap.pypa.io/get-pip.py', 'C:/get-pip.py') - - "c:/python27/python.exe C:/get-pip.py --no-use-wheel setuptools tox virtualenv" + - "c:/python27/python.exe C:/get-pip.py --no-use-wheel tox virtualenv" test_script: - "c:/python27/Scripts/tox.exe --version" - "c:/python27/Scripts/virtualenv --version" diff --git a/appveyor.yml b/appveyor.yml index fef9727..555e2cc 100644 --- a/appveyor.yml +++ b/appveyor.yml @@ -15,7 +15,7 @@ init: - ps: "ls C:/Python*" install: - ps: (new-object net.webclient).DownloadFile('https://bootstrap.pypa.io/get-pip.py', 'C:/get-pip.py') - - "c:/python27/python.exe C:/get-pip.py --no-use-wheel setuptools tox virtualenv" + - "c:/python27/python.exe C:/get-pip.py --no-use-wheel tox virtualenv" test_script: - "c:/python27/Scripts/tox.exe --version" - "c:/python27/Scripts/virtualenv --version" From b69cfef9b95628d3cf8d84a2e8f4873b98e9a069 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ionel=20Cristian=20M=C4=83rie=C8=99?= Date: Sun, 15 Jun 2014 06:14:42 +0300 Subject: [PATCH 008/179] Bootstrap setuptools first. --- appveyor.tmpl.yml | 4 ++-- appveyor.yml | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/appveyor.tmpl.yml b/appveyor.tmpl.yml index 691598c..047433d 100644 --- a/appveyor.tmpl.yml +++ b/appveyor.tmpl.yml @@ -9,8 +9,8 @@ init: - "ECHO %TOXENV%" - ps: "ls C:/Python*" install: - - ps: (new-object net.webclient).DownloadFile('https://bootstrap.pypa.io/get-pip.py', 'C:/get-pip.py') - - "c:/python27/python.exe C:/get-pip.py --no-use-wheel tox virtualenv" + - ps: (Invoke-WebRequest https://bootstrap.pypa.io/ez_setup.py).Content | python - + - ps: (Invoke-WebRequest https://bootstrap.pypa.io/get-pip.py).Content | python - tox virtualenv test_script: - "c:/python27/Scripts/tox.exe --version" - "c:/python27/Scripts/virtualenv --version" diff --git a/appveyor.yml b/appveyor.yml index 555e2cc..2e77897 100644 --- a/appveyor.yml +++ b/appveyor.yml @@ -14,8 +14,8 @@ init: - "ECHO %TOXENV%" - ps: "ls C:/Python*" install: - - ps: (new-object net.webclient).DownloadFile('https://bootstrap.pypa.io/get-pip.py', 'C:/get-pip.py') - - "c:/python27/python.exe C:/get-pip.py --no-use-wheel tox virtualenv" + - ps: (Invoke-WebRequest https://bootstrap.pypa.io/ez_setup.py).Content | python - + - ps: (Invoke-WebRequest https://bootstrap.pypa.io/get-pip.py).Content | python - tox virtualenv test_script: - "c:/python27/Scripts/tox.exe --version" - "c:/python27/Scripts/virtualenv --version" From 6e7aae94301623c2587fdbff7b5e9d173101b5e0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ionel=20Cristian=20M=C4=83rie=C8=99?= Date: Sun, 15 Jun 2014 15:38:44 +0300 Subject: [PATCH 009/179] Fix bootstrap commands. --- MANIFEST.in | 1 + appveyor.tmpl.yml | 7 ++++--- appveyor.yml | 7 ++++--- 3 files changed, 9 insertions(+), 6 deletions(-) diff --git a/MANIFEST.in b/MANIFEST.in index 235320c..4255b3c 100644 --- a/MANIFEST.in +++ b/MANIFEST.in @@ -18,6 +18,7 @@ include README.rst include configure include tox.ini tox.tmpl.ini include .travis.yml .travis.tmpl.yml +include appveyor.tmpl.yml appveyor.yml include conftest.py global-exclude *.py[co] __pycache__ diff --git a/appveyor.tmpl.yml b/appveyor.tmpl.yml index 047433d..2b233ab 100644 --- a/appveyor.tmpl.yml +++ b/appveyor.tmpl.yml @@ -9,9 +9,10 @@ init: - "ECHO %TOXENV%" - ps: "ls C:/Python*" install: - - ps: (Invoke-WebRequest https://bootstrap.pypa.io/ez_setup.py).Content | python - - - ps: (Invoke-WebRequest https://bootstrap.pypa.io/get-pip.py).Content | python - tox virtualenv + - ps: (Invoke-WebRequest "https://bitbucket.org/pypa/setuptools/raw/bootstrap/ez_setup.py").Content | python - tox pip virtualenv + - ps: (Invoke-WebRequest https://bootstrap.pypa.io/get-pip.py).RawContent | python - tox virtualenv test_script: - - "c:/python27/Scripts/tox.exe --version" + - "c:/python27/Scripts/tox --version" - "c:/python27/Scripts/virtualenv --version" - "c:/python27/Scripts/pip --version" + - "c:/python27/Scripts/tox" \ No newline at end of file diff --git a/appveyor.yml b/appveyor.yml index 2e77897..41ff0b8 100644 --- a/appveyor.yml +++ b/appveyor.yml @@ -14,9 +14,10 @@ init: - "ECHO %TOXENV%" - ps: "ls C:/Python*" install: - - ps: (Invoke-WebRequest https://bootstrap.pypa.io/ez_setup.py).Content | python - - - ps: (Invoke-WebRequest https://bootstrap.pypa.io/get-pip.py).Content | python - tox virtualenv + - ps: (Invoke-WebRequest "https://bitbucket.org/pypa/setuptools/raw/bootstrap/ez_setup.py").Content | python - tox pip virtualenv + - ps: (Invoke-WebRequest https://bootstrap.pypa.io/get-pip.py).RawContent | python - tox virtualenv test_script: - - "c:/python27/Scripts/tox.exe --version" + - "c:/python27/Scripts/tox --version" - "c:/python27/Scripts/virtualenv --version" - "c:/python27/Scripts/pip --version" + - "c:/python27/Scripts/tox" \ No newline at end of file From 5e2e61c2df07d8b534b64724a3c46d8e5fdccf37 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ionel=20Cristian=20M=C4=83rie=C8=99?= Date: Sun, 15 Jun 2014 16:16:34 +0300 Subject: [PATCH 010/179] Update setup scripts. --- appveyor.tmpl.yml | 8 ++++++-- appveyor.yml | 8 ++++++-- 2 files changed, 12 insertions(+), 4 deletions(-) diff --git a/appveyor.tmpl.yml b/appveyor.tmpl.yml index 2b233ab..30b39b6 100644 --- a/appveyor.tmpl.yml +++ b/appveyor.tmpl.yml @@ -9,8 +9,12 @@ init: - "ECHO %TOXENV%" - ps: "ls C:/Python*" install: - - ps: (Invoke-WebRequest "https://bitbucket.org/pypa/setuptools/raw/bootstrap/ez_setup.py").Content | python - tox pip virtualenv - - ps: (Invoke-WebRequest https://bootstrap.pypa.io/get-pip.py).RawContent | python - tox virtualenv + - "cd C:/Python27" + - ps: Invoke-WebRequest "https://bitbucket.org/pypa/setuptools/raw/bootstrap/ez_setup.py" -OutFile "ez_setup.py" + - ps: Invoke-WebRequest "https://bootstrap.pypa.io/get-pip.py" -OutFile "get-pip.py" + - python ez_setup.py + - python get-pip.py + - Scripts/pip install tox test_script: - "c:/python27/Scripts/tox --version" - "c:/python27/Scripts/virtualenv --version" diff --git a/appveyor.yml b/appveyor.yml index 41ff0b8..1bf4dd0 100644 --- a/appveyor.yml +++ b/appveyor.yml @@ -14,8 +14,12 @@ init: - "ECHO %TOXENV%" - ps: "ls C:/Python*" install: - - ps: (Invoke-WebRequest "https://bitbucket.org/pypa/setuptools/raw/bootstrap/ez_setup.py").Content | python - tox pip virtualenv - - ps: (Invoke-WebRequest https://bootstrap.pypa.io/get-pip.py).RawContent | python - tox virtualenv + - "cd C:/Python27" + - ps: Invoke-WebRequest "https://bitbucket.org/pypa/setuptools/raw/bootstrap/ez_setup.py" -OutFile "ez_setup.py" + - ps: Invoke-WebRequest "https://bootstrap.pypa.io/get-pip.py" -OutFile "get-pip.py" + - python ez_setup.py + - python get-pip.py + - Scripts/pip install tox test_script: - "c:/python27/Scripts/tox --version" - "c:/python27/Scripts/virtualenv --version" From 65b92810e4a98bf4399c46a6658270a0e644d7fc Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ionel=20Cristian=20M=C4=83rie=C8=99?= Date: Sun, 15 Jun 2014 16:17:58 +0300 Subject: [PATCH 011/179] Don't bother with coveralls for now. --- appveyor.tmpl.yml | 2 +- appveyor.yml | 16 ++++++++-------- 2 files changed, 9 insertions(+), 9 deletions(-) diff --git a/appveyor.tmpl.yml b/appveyor.tmpl.yml index 30b39b6..baf3360 100644 --- a/appveyor.tmpl.yml +++ b/appveyor.tmpl.yml @@ -3,7 +3,7 @@ environment: matrix: - TOXENV: check {% for env, config in tox_environments|dictsort %}{% if '2.7' in env or '3.4' in env %} - - TOXENV: "{{ env }}{% if config.cover %},coveralls{% endif %}" + - TOXENV: "{{ env }} {% endif %}{% endfor %} init: - "ECHO %TOXENV%" diff --git a/appveyor.yml b/appveyor.yml index 1bf4dd0..12ae1af 100644 --- a/appveyor.yml +++ b/appveyor.yml @@ -2,14 +2,14 @@ build: false environment: matrix: - TOXENV: check - - TOXENV: "2.7-trollius-mysql,coveralls" - - TOXENV: "2.7-trollius-mysql-ASPECTLIB_DEBUG=yes,coveralls" - - TOXENV: "2.7-trollius-mysql-nocover" - - TOXENV: "2.7-trollius-mysql-nocover-ASPECTLIB_DEBUG=yes" - - TOXENV: "3.4,coveralls" - - TOXENV: "3.4-ASPECTLIB_DEBUG=yes,coveralls" - - TOXENV: "3.4-nocover" - - TOXENV: "3.4-nocover-ASPECTLIB_DEBUG=yes" + - TOXENV: "2.7-trollius-mysql + - TOXENV: "2.7-trollius-mysql-ASPECTLIB_DEBUG=yes + - TOXENV: "2.7-trollius-mysql-nocover + - TOXENV: "2.7-trollius-mysql-nocover-ASPECTLIB_DEBUG=yes + - TOXENV: "3.4 + - TOXENV: "3.4-ASPECTLIB_DEBUG=yes + - TOXENV: "3.4-nocover + - TOXENV: "3.4-nocover-ASPECTLIB_DEBUG=yes init: - "ECHO %TOXENV%" - ps: "ls C:/Python*" From 7e09f03fd45e438a71d958f51ebc482267c71fb8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ionel=20Cristian=20M=C4=83rie=C8=99?= Date: Sun, 15 Jun 2014 16:19:21 +0300 Subject: [PATCH 012/179] Wrong slash. --- appveyor.tmpl.yml | 2 +- appveyor.yml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/appveyor.tmpl.yml b/appveyor.tmpl.yml index baf3360..9ae8c0b 100644 --- a/appveyor.tmpl.yml +++ b/appveyor.tmpl.yml @@ -14,7 +14,7 @@ install: - ps: Invoke-WebRequest "https://bootstrap.pypa.io/get-pip.py" -OutFile "get-pip.py" - python ez_setup.py - python get-pip.py - - Scripts/pip install tox + - Scripts\pip install tox test_script: - "c:/python27/Scripts/tox --version" - "c:/python27/Scripts/virtualenv --version" diff --git a/appveyor.yml b/appveyor.yml index 12ae1af..b1f38f1 100644 --- a/appveyor.yml +++ b/appveyor.yml @@ -19,7 +19,7 @@ install: - ps: Invoke-WebRequest "https://bootstrap.pypa.io/get-pip.py" -OutFile "get-pip.py" - python ez_setup.py - python get-pip.py - - Scripts/pip install tox + - Scripts\pip install tox test_script: - "c:/python27/Scripts/tox --version" - "c:/python27/Scripts/virtualenv --version" From db50777f8391714d5d19397e0447aad37d782084 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ionel=20Cristian=20M=C4=83rie=C8=99?= Date: Sun, 15 Jun 2014 16:21:20 +0300 Subject: [PATCH 013/179] Maybe quoting fixes this. --- appveyor.tmpl.yml | 2 +- appveyor.yml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/appveyor.tmpl.yml b/appveyor.tmpl.yml index 9ae8c0b..d2bdbda 100644 --- a/appveyor.tmpl.yml +++ b/appveyor.tmpl.yml @@ -14,7 +14,7 @@ install: - ps: Invoke-WebRequest "https://bootstrap.pypa.io/get-pip.py" -OutFile "get-pip.py" - python ez_setup.py - python get-pip.py - - Scripts\pip install tox + - "Scripts/pip install tox" test_script: - "c:/python27/Scripts/tox --version" - "c:/python27/Scripts/virtualenv --version" diff --git a/appveyor.yml b/appveyor.yml index b1f38f1..5935e15 100644 --- a/appveyor.yml +++ b/appveyor.yml @@ -19,7 +19,7 @@ install: - ps: Invoke-WebRequest "https://bootstrap.pypa.io/get-pip.py" -OutFile "get-pip.py" - python ez_setup.py - python get-pip.py - - Scripts\pip install tox + - "Scripts/pip install tox" test_script: - "c:/python27/Scripts/tox --version" - "c:/python27/Scripts/virtualenv --version" From 32efeeee37ef0ad3bcc281d430e7a80e4a905f0b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ionel=20Cristian=20M=C4=83rie=C8=99?= Date: Sun, 15 Jun 2014 16:22:10 +0300 Subject: [PATCH 014/179] Ooops. --- appveyor.tmpl.yml | 2 +- appveyor.yml | 16 ++++++++-------- 2 files changed, 9 insertions(+), 9 deletions(-) diff --git a/appveyor.tmpl.yml b/appveyor.tmpl.yml index d2bdbda..502443b 100644 --- a/appveyor.tmpl.yml +++ b/appveyor.tmpl.yml @@ -3,7 +3,7 @@ environment: matrix: - TOXENV: check {% for env, config in tox_environments|dictsort %}{% if '2.7' in env or '3.4' in env %} - - TOXENV: "{{ env }} + - TOXENV: "{{ env }}" {% endif %}{% endfor %} init: - "ECHO %TOXENV%" diff --git a/appveyor.yml b/appveyor.yml index 5935e15..b2e1fdb 100644 --- a/appveyor.yml +++ b/appveyor.yml @@ -2,14 +2,14 @@ build: false environment: matrix: - TOXENV: check - - TOXENV: "2.7-trollius-mysql - - TOXENV: "2.7-trollius-mysql-ASPECTLIB_DEBUG=yes - - TOXENV: "2.7-trollius-mysql-nocover - - TOXENV: "2.7-trollius-mysql-nocover-ASPECTLIB_DEBUG=yes - - TOXENV: "3.4 - - TOXENV: "3.4-ASPECTLIB_DEBUG=yes - - TOXENV: "3.4-nocover - - TOXENV: "3.4-nocover-ASPECTLIB_DEBUG=yes + - TOXENV: "2.7-trollius-mysql" + - TOXENV: "2.7-trollius-mysql-ASPECTLIB_DEBUG=yes" + - TOXENV: "2.7-trollius-mysql-nocover" + - TOXENV: "2.7-trollius-mysql-nocover-ASPECTLIB_DEBUG=yes" + - TOXENV: "3.4" + - TOXENV: "3.4-ASPECTLIB_DEBUG=yes" + - TOXENV: "3.4-nocover" + - TOXENV: "3.4-nocover-ASPECTLIB_DEBUG=yes" init: - "ECHO %TOXENV%" - ps: "ls C:/Python*" From 7c1308c18f3cd87b09555f050f1cfad86fe6a950 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ionel=20Cristian=20M=C4=83rie=C8=99?= Date: Sun, 15 Jun 2014 16:28:42 +0300 Subject: [PATCH 015/179] Update appveyor conf. --- appveyor.tmpl.yml | 3 ++- appveyor.yml | 3 ++- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/appveyor.tmpl.yml b/appveyor.tmpl.yml index 502443b..1553db1 100644 --- a/appveyor.tmpl.yml +++ b/appveyor.tmpl.yml @@ -14,7 +14,8 @@ install: - ps: Invoke-WebRequest "https://bootstrap.pypa.io/get-pip.py" -OutFile "get-pip.py" - python ez_setup.py - python get-pip.py - - "Scripts/pip install tox" + - cd Scripts + - pip install tox test_script: - "c:/python27/Scripts/tox --version" - "c:/python27/Scripts/virtualenv --version" diff --git a/appveyor.yml b/appveyor.yml index b2e1fdb..00f52db 100644 --- a/appveyor.yml +++ b/appveyor.yml @@ -19,7 +19,8 @@ install: - ps: Invoke-WebRequest "https://bootstrap.pypa.io/get-pip.py" -OutFile "get-pip.py" - python ez_setup.py - python get-pip.py - - "Scripts/pip install tox" + - cd Scripts + - pip install tox test_script: - "c:/python27/Scripts/tox --version" - "c:/python27/Scripts/virtualenv --version" From e9f4fe589e4c8fed389f069937f47716312e150f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ionel=20Cristian=20M=C4=83rie=C8=99?= Date: Sun, 15 Jun 2014 16:38:19 +0300 Subject: [PATCH 016/179] Dont change dirs. --- appveyor.tmpl.yml | 12 +++++------- appveyor.yml | 12 +++++------- 2 files changed, 10 insertions(+), 14 deletions(-) diff --git a/appveyor.tmpl.yml b/appveyor.tmpl.yml index 1553db1..843bc5a 100644 --- a/appveyor.tmpl.yml +++ b/appveyor.tmpl.yml @@ -9,13 +9,11 @@ init: - "ECHO %TOXENV%" - ps: "ls C:/Python*" install: - - "cd C:/Python27" - - ps: Invoke-WebRequest "https://bitbucket.org/pypa/setuptools/raw/bootstrap/ez_setup.py" -OutFile "ez_setup.py" - - ps: Invoke-WebRequest "https://bootstrap.pypa.io/get-pip.py" -OutFile "get-pip.py" - - python ez_setup.py - - python get-pip.py - - cd Scripts - - pip install tox + - ps: Invoke-WebRequest "https://bitbucket.org/pypa/setuptools/raw/bootstrap/ez_setup.py" -OutFile "c:/ez_setup.py" + - ps: Invoke-WebRequest "https://bootstrap.pypa.io/get-pip.py" -OutFile "c:/get-pip.py" + - "c:/python27/python c:/ez_setup.py" + - "c:/python27/python c:/get-pip.py" + - "c:/python27/Scripts/pip install tox" test_script: - "c:/python27/Scripts/tox --version" - "c:/python27/Scripts/virtualenv --version" diff --git a/appveyor.yml b/appveyor.yml index 00f52db..159121a 100644 --- a/appveyor.yml +++ b/appveyor.yml @@ -14,13 +14,11 @@ init: - "ECHO %TOXENV%" - ps: "ls C:/Python*" install: - - "cd C:/Python27" - - ps: Invoke-WebRequest "https://bitbucket.org/pypa/setuptools/raw/bootstrap/ez_setup.py" -OutFile "ez_setup.py" - - ps: Invoke-WebRequest "https://bootstrap.pypa.io/get-pip.py" -OutFile "get-pip.py" - - python ez_setup.py - - python get-pip.py - - cd Scripts - - pip install tox + - ps: Invoke-WebRequest "https://bitbucket.org/pypa/setuptools/raw/bootstrap/ez_setup.py" -OutFile "c:/ez_setup.py" + - ps: Invoke-WebRequest "https://bootstrap.pypa.io/get-pip.py" -OutFile "c:/get-pip.py" + - "c:/python27/python c:/ez_setup.py" + - "c:/python27/python c:/get-pip.py" + - "c:/python27/Scripts/pip install tox" test_script: - "c:/python27/Scripts/tox --version" - "c:/python27/Scripts/virtualenv --version" From 89baf049c252acdcf1c54f628d6eeed978a2d703 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ionel=20Cristian=20M=C4=83rie=C8=99?= Date: Sun, 15 Jun 2014 16:54:58 +0300 Subject: [PATCH 017/179] Fix the 3.4 deps. --- setup.cfg | 2 +- tox.ini | 4 ---- 2 files changed, 1 insertion(+), 5 deletions(-) diff --git a/setup.cfg b/setup.cfg index 543a869..454792c 100644 --- a/setup.cfg +++ b/setup.cfg @@ -32,7 +32,7 @@ python_versions = dependencies = trollius-mysql: trollius MySQL-python !python_versions[3.*] asyncio: asyncio &python_versions[3.3] - :- &python_versions[3.4] + : &python_versions[3.4] coverage_flags = : true diff --git a/tox.ini b/tox.ini index cf8c025..e6d925a 100644 --- a/tox.ini +++ b/tox.ini @@ -268,7 +268,6 @@ deps = {[testenv]deps} coverage pytest-cov - - [testenv:3.4-ASPECTLIB_DEBUG=yes] basepython = python3.4 @@ -283,7 +282,6 @@ deps = {[testenv]deps} coverage pytest-cov - - [testenv:3.4-nocover] basepython = python3.4 @@ -291,7 +289,6 @@ deps = {[testenv]deps} coverage pytest-cov - - [testenv:3.4-nocover-ASPECTLIB_DEBUG=yes] basepython = python3.4 @@ -302,7 +299,6 @@ deps = {[testenv]deps} coverage pytest-cov - - [testenv:pypy-trollius-mysql] basepython = pypy From ac7d2b677568d249e7b99418ba1fdc4e44eaa831 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ionel=20Cristian=20M=C4=83rie=C8=99?= Date: Sun, 15 Jun 2014 17:00:16 +0300 Subject: [PATCH 018/179] Disable greendots. --- tox.ini | 1 - tox.tmpl.ini | 1 - 2 files changed, 2 deletions(-) diff --git a/tox.ini b/tox.ini index e6d925a..36e2814 100644 --- a/tox.ini +++ b/tox.ini @@ -31,7 +31,6 @@ setenv = PYTHONUNBUFFERED=yes deps = pytest - pytest-greendots pytest-capturelog process-tests nose diff --git a/tox.tmpl.ini b/tox.tmpl.ini index d982c9e..937128d 100644 --- a/tox.tmpl.ini +++ b/tox.tmpl.ini @@ -14,7 +14,6 @@ setenv = PYTHONUNBUFFERED=yes deps = pytest - pytest-greendots pytest-capturelog process-tests nose From 7a16df5a88515b36eabe8d47fcf5acb95d252e72 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ionel=20Cristian=20M=C4=83rie=C8=99?= Date: Wed, 18 Jun 2014 01:39:32 +0300 Subject: [PATCH 019/179] Simplify the docs (and doctests) in the testing section. --- docs/testing.rst | 102 ++++++++++++++++------------------------------- 1 file changed, 34 insertions(+), 68 deletions(-) diff --git a/docs/testing.rst b/docs/testing.rst index 63a8889..1cb4f7b 100644 --- a/docs/testing.rst +++ b/docs/testing.rst @@ -132,7 +132,7 @@ We can start with some existing test data in the filesystem:: Write an empty story and examine the output:: >>> from aspectlib.test import Story - >>> with Story(os, methods="^(?!error)[a-z]+$") as story: + >>> with Story(['os.path.isdir', 'os.listdir']) as story: ... pass >>> with story.replay(strict=False) as replay: ... tree('some') @@ -142,43 +142,30 @@ Write an empty story and examine the output:: │ └── file.txt └── empty STORY/REPLAY DIFF: - --- expected... - +++ actual... - @@ ... @@ + --- expected + +++ actual + @@ -0,0 +1,8 @@ +os.listdir('some') == ['test'] # returns - +os.stat('some/test') == os.stat_result((...)) # returns - +os.listdir('some/test') == [...'dir'...] # returns - +os.stat('some/test/dir') == os.stat_result((...)) # returns - +os.listdir('some/test/dir') == ['file.txt'] # returns - +os.stat('some/test/dir/file.txt') == os.stat_result((...)) # returns - +os.stat('some/test/empty') == os.stat_result((...)) # returns - +os.listdir('some/test/empty') == [] # returns + +...isdir('some...test') == True # returns + +os.listdir('some...test') == [...'empty'...] # returns + +...isdir('some...test...dir') == True # returns + +os.listdir('some...test...dir') == ['file.txt'] # returns + +...isdir('some...test...dir...file.txt') == False # returns + +...isdir('some...test...empty') == True # returns + +os.listdir('some...test...empty') == [] # returns ACTUAL: os.listdir('some') == ['test'] # returns - os.stat('some/test') == os.stat_result((...)) # returns - os.listdir('some/test') == [...'dir'...] # returns - os.stat('some/test/dir') == os.stat_result((...)) # returns - os.listdir('some/test/dir') == ['file.txt'] # returns - os.stat('some/test/dir/file.txt') == os.stat_result((...)) # returns - os.stat('some/test/empty') == os.stat_result((...)) # returns - os.listdir('some/test/empty') == [] # returns + ...isdir('some...test') == True # returns + os.listdir('some...test') == [...'empty'...] # returns + ...isdir('some...test...dir') == True # returns + os.listdir('some...test...dir') == ['file.txt'] # returns + ...isdir('some...test...dir...file.txt') == False # returns + ...isdir('some...test...empty') == True # returns + os.listdir('some...test...empty') == [] # returns .. - We can quickly get whatever we would need to put in the story with :obj:`aspectlib.test.Replay.unexpected`:: - - >>> print(replay.unexpected) - os.listdir('some') == ['test'] # returns - os.stat('some/test') == os.stat_result((...)) # returns - os.listdir('some/test') == [...'dir'...] # returns - os.stat('some/test/dir') == os.stat_result((...)) # returns - os.listdir('some/test/dir') == ['file.txt'] # returns - os.stat('some/test/dir/file.txt') == os.stat_result((...)) # returns - os.stat('some/test/empty') == os.stat_result((...)) # returns - os.listdir('some/test/empty') == [] # returns - - Now we can remove the test directories and fill the story:: >>> import shutil @@ -188,17 +175,18 @@ Now we can remove the test directories and fill the story:: The story:: - >>> with Story(os, methods="^(?!error)[a-z]+$") as story: - ... os.listdir('some') == ['test'] - ... os.stat('some/test') == os.stat_result((16893, 6691875, 2049, 3, 1000, 1000, 4096, 1399131539, 1399131539, 1399131539)) - ... os.listdir('some/test') == ['empty', 'dir'] # returns - ... os.stat('some/test/dir') == os.stat_result((16893, 6691876, 2049, 2, 1000, 1000, 4096, 1399131539, 1399131539, 1399131539)) - ... os.listdir('some/test/dir') == ['file.txt'] - ... os.stat('some/test/dir/file.txt') == os.stat_result((33204, 6691877, 2049, 1, 1000, 1000, 9, 1399131539, 1399131539, 1399131539)) - ... os.stat('some/test/empty') == os.stat_result((16893, 6691877, 2049, 2, 1000, 1000, 4096, 1399132977, 1399132977, 1399132977)) # returns - ... os.listdir('some/test/empty') == [] # returns + >>> with Story(['os.path.isdir', 'os.listdir']) as story: + ... os.listdir('some') == ['test'] # returns + ... os.path.isdir(os.path.join('some', 'test')) == True + ... os.listdir(os.path.join('some', 'test')) == ['dir', 'empty'] + ... os.path.isdir(os.path.join('some', 'test', 'dir')) == True + ... os.listdir(os.path.join('some', 'test', 'dir')) == ['file.txt'] + ... os.path.isdir(os.path.join('some', 'test', 'dir', 'file.txt')) == False + ... os.path.isdir(os.path.join('some', 'test', 'empty')) == True + ... os.listdir(os.path.join('some', 'test', 'empty')) == [] - And the `strict` :obj:`replay `:: + We can also disable proxying in :obj:`replay ` so that the tested code can't use the + real functions:: >>> with story.replay(proxy=False) as replay: ... tree('some') @@ -208,30 +196,8 @@ Now we can remove the test directories and fill the story:: │ └── file.txt └── empty - -If we diverge a bit from the story (or we'd have some unexpected change in the ``tree`` function) we'd get something -like this:: - - >>> with Story(os, methods="^(?!error)[a-z]+$") as story: - ... os.listdir('some') == ['test'] - ... os.listdir('bogus') == ['some bogus directory'] - ... os.stat('some/test') == os.stat_result((16893, 6691875, 2049, 3, 1000, 1000, 4096, 1399131539, 1399131539, 1399131539)) - ... os.listdir('some/test') == ['empty', 'dir'] # returns - ... os.stat('some/test/dir') == os.stat_result((16893, 6691876, 2049, 2, 1000, 1000, 4096, 1399131539, 1399131539, 1399131539)) - ... os.listdir('some/test/dir') == ['file.txt'] - ... os.stat('some/test/dir/file.txt') == os.stat_result((33204, 6691877, 2049, 1, 1000, 1000, 9, 1399131539, 1399131539, 1399131539)) - ... os.stat('some/test/empty') == os.stat_result((16893, 6691877, 2049, 2, 1000, 1000, 4096, 1399132977, 1399132977, 1399132977)) # returns - ... os.listdir('some/test/empty') == [] # returns - >>> with story.replay(proxy=False) as replay: - ... tree('some') - Traceback (most recent call last): - ... - AssertionError: --- expected... - +++ actual... - @@ ... @@ - os.listdir('some') == ['test'] # returns - -os.listdir('bogus') == ['some bogus directory'] # returns - os.stat('some/test') == os.stat_result((16893, 6691875, 2049, 3, 1000, 1000, 4096, 1399131539, 1399131539, 1399131539)) # returns - os.listdir('some/test') == [...'dir'...] # returns - os.stat('some/test/dir') == os.stat_result((16893, 6691876, 2049, 2, 1000, 1000, 4096, 1399131539, 1399131539, 1399131539)) # returns - + >>> with story.replay(proxy=False, strict=False) as replay: + ... tree('missing-from-story') + Traceback (most recent call last): + ... + AssertionError: Unexpected call to None/os.listdir with args:'missing-from-story' kwargs: From 81744cd1c40071b1ac1e0215b52bec6eb300a596 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ionel=20Cristian=20M=C4=83rie=C8=99?= Date: Wed, 18 Jun 2014 04:59:56 +0300 Subject: [PATCH 020/179] Update test config. --- .travis.yml | 48 +++++---- appveyor.tmpl.yml | 4 +- appveyor.yml | 14 +-- setup.cfg | 7 +- setup.py | 2 +- tox.ini | 254 +++++++++++++++++++++++++++++++++++++--------- tox.tmpl.ini | 2 +- 7 files changed, 250 insertions(+), 81 deletions(-) diff --git a/.travis.yml b/.travis.yml index 9fb8059..c968408 100644 --- a/.travis.yml +++ b/.travis.yml @@ -5,26 +5,38 @@ env: LD_PRELOAD=/lib/x86_64-linux-gnu/libSegFault.so matrix: - TOXENV=check - - TOXENV=2.6-trollius-mysql,coveralls - - TOXENV=2.6-trollius-mysql-ASPECTLIB_DEBUG=yes,coveralls - - TOXENV=2.6-trollius-mysql-nocover - - TOXENV=2.6-trollius-mysql-nocover-ASPECTLIB_DEBUG=yes - - TOXENV=2.7-trollius-mysql,coveralls - - TOXENV=2.7-trollius-mysql-ASPECTLIB_DEBUG=yes,coveralls - - TOXENV=2.7-trollius-mysql-nocover - - TOXENV=2.7-trollius-mysql-nocover-ASPECTLIB_DEBUG=yes - - TOXENV=3.3-asyncio,coveralls - - TOXENV=3.3-asyncio-ASPECTLIB_DEBUG=yes,coveralls - - TOXENV=3.3-asyncio-nocover - - TOXENV=3.3-asyncio-nocover-ASPECTLIB_DEBUG=yes + - TOXENV=2.6,coveralls + - TOXENV=2.6-debug,coveralls + - TOXENV=2.6-nix,coveralls + - TOXENV=2.6-nix-debug,coveralls + - TOXENV=2.6-nix-nocover + - TOXENV=2.6-nix-nocover-debug + - TOXENV=2.6-nocover + - TOXENV=2.6-nocover-debug + - TOXENV=2.7,coveralls + - TOXENV=2.7-debug,coveralls + - TOXENV=2.7-nix,coveralls + - TOXENV=2.7-nix-debug,coveralls + - TOXENV=2.7-nix-nocover + - TOXENV=2.7-nix-nocover-debug + - TOXENV=2.7-nocover + - TOXENV=2.7-nocover-debug + - TOXENV=3.3,coveralls + - TOXENV=3.3-debug,coveralls + - TOXENV=3.3-nocover + - TOXENV=3.3-nocover-debug - TOXENV=3.4,coveralls - - TOXENV=3.4-ASPECTLIB_DEBUG=yes,coveralls + - TOXENV=3.4-debug,coveralls - TOXENV=3.4-nocover - - TOXENV=3.4-nocover-ASPECTLIB_DEBUG=yes - - TOXENV=pypy-trollius-mysql,coveralls - - TOXENV=pypy-trollius-mysql-ASPECTLIB_DEBUG=yes,coveralls - - TOXENV=pypy-trollius-mysql-nocover - - TOXENV=pypy-trollius-mysql-nocover-ASPECTLIB_DEBUG=yes + - TOXENV=3.4-nocover-debug + - TOXENV=pypy,coveralls + - TOXENV=pypy-debug,coveralls + - TOXENV=pypy-nix,coveralls + - TOXENV=pypy-nix-debug,coveralls + - TOXENV=pypy-nix-nocover + - TOXENV=pypy-nix-nocover-debug + - TOXENV=pypy-nocover + - TOXENV=pypy-nocover-debug before_install: - python --version - virtualenv --version diff --git a/appveyor.tmpl.yml b/appveyor.tmpl.yml index 843bc5a..6963e28 100644 --- a/appveyor.tmpl.yml +++ b/appveyor.tmpl.yml @@ -2,7 +2,7 @@ build: false environment: matrix: - TOXENV: check -{% for env, config in tox_environments|dictsort %}{% if '2.7' in env or '3.4' in env %} +{% for env, config in tox_environments|dictsort %}{% if ('2.7' in env or '3.4' in env) and 'nix' not in env %} - TOXENV: "{{ env }}" {% endif %}{% endfor %} init: @@ -18,4 +18,4 @@ test_script: - "c:/python27/Scripts/tox --version" - "c:/python27/Scripts/virtualenv --version" - "c:/python27/Scripts/pip --version" - - "c:/python27/Scripts/tox" \ No newline at end of file + - "c:/python27/Scripts/tox" diff --git a/appveyor.yml b/appveyor.yml index 159121a..c41cffd 100644 --- a/appveyor.yml +++ b/appveyor.yml @@ -2,14 +2,14 @@ build: false environment: matrix: - TOXENV: check - - TOXENV: "2.7-trollius-mysql" - - TOXENV: "2.7-trollius-mysql-ASPECTLIB_DEBUG=yes" - - TOXENV: "2.7-trollius-mysql-nocover" - - TOXENV: "2.7-trollius-mysql-nocover-ASPECTLIB_DEBUG=yes" + - TOXENV: "2.7" + - TOXENV: "2.7-debug" + - TOXENV: "2.7-nocover" + - TOXENV: "2.7-nocover-debug" - TOXENV: "3.4" - - TOXENV: "3.4-ASPECTLIB_DEBUG=yes" + - TOXENV: "3.4-debug" - TOXENV: "3.4-nocover" - - TOXENV: "3.4-nocover-ASPECTLIB_DEBUG=yes" + - TOXENV: "3.4-nocover-debug" init: - "ECHO %TOXENV%" - ps: "ls C:/Python*" @@ -23,4 +23,4 @@ test_script: - "c:/python27/Scripts/tox --version" - "c:/python27/Scripts/virtualenv --version" - "c:/python27/Scripts/pip --version" - - "c:/python27/Scripts/tox" \ No newline at end of file + - "c:/python27/Scripts/tox" diff --git a/setup.cfg b/setup.cfg index 454792c..e836055 100644 --- a/setup.cfg +++ b/setup.cfg @@ -30,8 +30,9 @@ python_versions = pypy dependencies = - trollius-mysql: trollius MySQL-python !python_versions[3.*] - asyncio: asyncio &python_versions[3.3] + nix: trollius-mysql: trollius !python_versions[3.*] + : trollius !python_versions[3.*] + : asyncio &python_versions[3.3] : &python_versions[3.4] coverage_flags = @@ -39,5 +40,5 @@ coverage_flags = nocover: false environment_variables = - ASPECTLIB_DEBUG=yes + debug: ASPECTLIB_DEBUG=yes - diff --git a/setup.py b/setup.py index 4aa226a..a35be2c 100644 --- a/setup.py +++ b/setup.py @@ -1,4 +1,4 @@ -# -*- encoding: utf8 -*- +# -*- encoding: utf-8 -*- from setuptools import setup, find_packages import os diff --git a/tox.ini b/tox.ini index 36e2814..81b2c8a 100644 --- a/tox.ini +++ b/tox.ini @@ -2,26 +2,38 @@ envlist = clean, check, - 2.6-trollius-mysql, - 2.6-trollius-mysql-ASPECTLIB_DEBUG=yes, - 2.6-trollius-mysql-nocover, - 2.6-trollius-mysql-nocover-ASPECTLIB_DEBUG=yes, - 2.7-trollius-mysql, - 2.7-trollius-mysql-ASPECTLIB_DEBUG=yes, - 2.7-trollius-mysql-nocover, - 2.7-trollius-mysql-nocover-ASPECTLIB_DEBUG=yes, - 3.3-asyncio, - 3.3-asyncio-ASPECTLIB_DEBUG=yes, - 3.3-asyncio-nocover, - 3.3-asyncio-nocover-ASPECTLIB_DEBUG=yes, + 2.6, + 2.6-debug, + 2.6-nix, + 2.6-nix-debug, + 2.6-nix-nocover, + 2.6-nix-nocover-debug, + 2.6-nocover, + 2.6-nocover-debug, + 2.7, + 2.7-debug, + 2.7-nix, + 2.7-nix-debug, + 2.7-nix-nocover, + 2.7-nix-nocover-debug, + 2.7-nocover, + 2.7-nocover-debug, + 3.3, + 3.3-debug, + 3.3-nocover, + 3.3-nocover-debug, 3.4, - 3.4-ASPECTLIB_DEBUG=yes, + 3.4-debug, 3.4-nocover, - 3.4-nocover-ASPECTLIB_DEBUG=yes, - pypy-trollius-mysql, - pypy-trollius-mysql-ASPECTLIB_DEBUG=yes, - pypy-trollius-mysql-nocover, - pypy-trollius-mysql-nocover-ASPECTLIB_DEBUG=yes, + 3.4-nocover-debug, + pypy, + pypy-debug, + pypy-nix, + pypy-nix-debug, + pypy-nix-nocover, + pypy-nix-nocover-debug, + pypy-nocover, + pypy-nocover-debug, report, docs @@ -54,7 +66,7 @@ deps = whitelist_externals = rm commands = - rm -rf dist/docs + rm -rf dist/docs || rmdir /S /Q dist\docs sphinx-build -b html docs dist/docs sphinx-build -b linkcheck docs dist/docs usedevelop = true @@ -103,7 +115,7 @@ commands = coverage erase usedevelop = true deps = coverage -[testenv:2.6-trollius-mysql] +[testenv:2.6] basepython = python2.6 setenv = {[testenv]setenv} @@ -116,9 +128,8 @@ deps = coverage pytest-cov trollius - MySQL-python -[testenv:2.6-trollius-mysql-ASPECTLIB_DEBUG=yes] +[testenv:2.6-debug] basepython = python2.6 setenv = {[testenv]setenv} @@ -132,30 +143,108 @@ deps = coverage pytest-cov trollius - MySQL-python -[testenv:2.6-trollius-mysql-nocover] +[testenv:2.6-nix] basepython = python2.6 +setenv = + {[testenv]setenv} + WITH_COVERAGE=yes +usedevelop = true +commands = + {posargs:py.test --cov src -vv} +deps = + {[testenv]deps} + coverage + pytest-cov + trollius-mysql: + trollius + +[testenv:2.6-nix-debug] +basepython = python2.6 +setenv = + {[testenv]setenv} + ASPECTLIB_DEBUG=yes + WITH_COVERAGE=yes +usedevelop = true +commands = + {posargs:py.test --cov src -vv} +deps = + {[testenv]deps} + coverage + pytest-cov + trollius-mysql: + trollius + +[testenv:2.6-nix-nocover] +basepython = python2.6 +deps = + {[testenv]deps} + coverage + pytest-cov + trollius-mysql: + trollius + +[testenv:2.6-nix-nocover-debug] +basepython = python2.6 +setenv = + {[testenv]setenv} + ASPECTLIB_DEBUG=yes deps = {[testenv]deps} coverage pytest-cov + trollius-mysql: trollius - MySQL-python -[testenv:2.6-trollius-mysql-nocover-ASPECTLIB_DEBUG=yes] +[testenv:2.6-nocover] basepython = python2.6 +deps = + {[testenv]deps} + coverage + pytest-cov + trollius + +[testenv:2.6-nocover-debug] +basepython = python2.6 +setenv = + {[testenv]setenv} + ASPECTLIB_DEBUG=yes +deps = + {[testenv]deps} + coverage + pytest-cov + trollius + +[testenv:2.7] +basepython = python2.7 +setenv = + {[testenv]setenv} + WITH_COVERAGE=yes +usedevelop = true +commands = + {posargs:py.test --cov src -vv} +deps = + {[testenv]deps} + coverage + pytest-cov + trollius + +[testenv:2.7-debug] +basepython = python2.7 setenv = {[testenv]setenv} ASPECTLIB_DEBUG=yes + WITH_COVERAGE=yes +usedevelop = true +commands = + {posargs:py.test --cov src -vv} deps = {[testenv]deps} coverage pytest-cov trollius - MySQL-python -[testenv:2.7-trollius-mysql] +[testenv:2.7-nix] basepython = python2.7 setenv = {[testenv]setenv} @@ -167,10 +256,10 @@ deps = {[testenv]deps} coverage pytest-cov + trollius-mysql: trollius - MySQL-python -[testenv:2.7-trollius-mysql-ASPECTLIB_DEBUG=yes] +[testenv:2.7-nix-debug] basepython = python2.7 setenv = {[testenv]setenv} @@ -183,19 +272,19 @@ deps = {[testenv]deps} coverage pytest-cov + trollius-mysql: trollius - MySQL-python -[testenv:2.7-trollius-mysql-nocover] +[testenv:2.7-nix-nocover] basepython = python2.7 deps = {[testenv]deps} coverage pytest-cov + trollius-mysql: trollius - MySQL-python -[testenv:2.7-trollius-mysql-nocover-ASPECTLIB_DEBUG=yes] +[testenv:2.7-nix-nocover-debug] basepython = python2.7 setenv = {[testenv]setenv} @@ -204,10 +293,29 @@ deps = {[testenv]deps} coverage pytest-cov + trollius-mysql: trollius - MySQL-python -[testenv:3.3-asyncio] +[testenv:2.7-nocover] +basepython = python2.7 +deps = + {[testenv]deps} + coverage + pytest-cov + trollius + +[testenv:2.7-nocover-debug] +basepython = python2.7 +setenv = + {[testenv]setenv} + ASPECTLIB_DEBUG=yes +deps = + {[testenv]deps} + coverage + pytest-cov + trollius + +[testenv:3.3] basepython = python3.3 setenv = {[testenv]setenv} @@ -221,7 +329,7 @@ deps = pytest-cov asyncio -[testenv:3.3-asyncio-ASPECTLIB_DEBUG=yes] +[testenv:3.3-debug] basepython = python3.3 setenv = {[testenv]setenv} @@ -236,7 +344,7 @@ deps = pytest-cov asyncio -[testenv:3.3-asyncio-nocover] +[testenv:3.3-nocover] basepython = python3.3 deps = {[testenv]deps} @@ -244,7 +352,7 @@ deps = pytest-cov asyncio -[testenv:3.3-asyncio-nocover-ASPECTLIB_DEBUG=yes] +[testenv:3.3-nocover-debug] basepython = python3.3 setenv = {[testenv]setenv} @@ -268,7 +376,7 @@ deps = coverage pytest-cov -[testenv:3.4-ASPECTLIB_DEBUG=yes] +[testenv:3.4-debug] basepython = python3.4 setenv = {[testenv]setenv} @@ -289,7 +397,7 @@ deps = coverage pytest-cov -[testenv:3.4-nocover-ASPECTLIB_DEBUG=yes] +[testenv:3.4-nocover-debug] basepython = python3.4 setenv = {[testenv]setenv} @@ -299,7 +407,36 @@ deps = coverage pytest-cov -[testenv:pypy-trollius-mysql] +[testenv:pypy] +basepython = pypy +setenv = + {[testenv]setenv} + WITH_COVERAGE=yes +usedevelop = true +commands = + {posargs:py.test --cov src -vv} +deps = + {[testenv]deps} + coverage + pytest-cov + trollius + +[testenv:pypy-debug] +basepython = pypy +setenv = + {[testenv]setenv} + ASPECTLIB_DEBUG=yes + WITH_COVERAGE=yes +usedevelop = true +commands = + {posargs:py.test --cov src -vv} +deps = + {[testenv]deps} + coverage + pytest-cov + trollius + +[testenv:pypy-nix] basepython = pypy setenv = {[testenv]setenv} @@ -311,10 +448,10 @@ deps = {[testenv]deps} coverage pytest-cov + trollius-mysql: trollius - MySQL-python -[testenv:pypy-trollius-mysql-ASPECTLIB_DEBUG=yes] +[testenv:pypy-nix-debug] basepython = pypy setenv = {[testenv]setenv} @@ -327,19 +464,39 @@ deps = {[testenv]deps} coverage pytest-cov + trollius-mysql: + trollius + +[testenv:pypy-nix-nocover] +basepython = pypy +deps = + {[testenv]deps} + coverage + pytest-cov + trollius-mysql: + trollius + +[testenv:pypy-nix-nocover-debug] +basepython = pypy +setenv = + {[testenv]setenv} + ASPECTLIB_DEBUG=yes +deps = + {[testenv]deps} + coverage + pytest-cov + trollius-mysql: trollius - MySQL-python -[testenv:pypy-trollius-mysql-nocover] +[testenv:pypy-nocover] basepython = pypy deps = {[testenv]deps} coverage pytest-cov trollius - MySQL-python -[testenv:pypy-trollius-mysql-nocover-ASPECTLIB_DEBUG=yes] +[testenv:pypy-nocover-debug] basepython = pypy setenv = {[testenv]setenv} @@ -349,5 +506,4 @@ deps = coverage pytest-cov trollius - MySQL-python diff --git a/tox.tmpl.ini b/tox.tmpl.ini index 937128d..8b0902e 100644 --- a/tox.tmpl.ini +++ b/tox.tmpl.ini @@ -37,7 +37,7 @@ deps = whitelist_externals = rm commands = - rm -rf dist/docs + rm -rf dist/docs || rmdir /S /Q dist\docs sphinx-build -b html docs dist/docs sphinx-build -b linkcheck docs dist/docs usedevelop = true From 83599bbdc1df2ae041d80fc6f0d44fcc1e289a99 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ionel=20Cristian=20M=C4=83rie=C8=99?= Date: Wed, 18 Jun 2014 05:00:06 +0300 Subject: [PATCH 021/179] Fix this for windows. --- tests/test_integrations.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/tests/test_integrations.py b/tests/test_integrations.py index 17c3270..5ae32cb 100644 --- a/tests/test_integrations.py +++ b/tests/test_integrations.py @@ -24,8 +24,8 @@ except ImportError: from io import StringIO -LOG_TEST_SOCKET = r"""^\{_?socket(object)?\}.connect\(\('127.0.0.1', 1\)\) +<<< .*tests/test_integrations.py:\d+:test_socket.* -\{_?socket(object)?\}.connect \~ raised .*(ConnectionRefusedError|error)\((10061|111), '.*refused.*'\)\n$""" +LOG_TEST_SOCKET = r"""^\{_?socket(object)?\}.connect\(\('127.0.0.1', 1\)\) +<<< .*tests[\/]test_integrations.py:\d+:test_socket.* +\{_?socket(object)?\}.connect \~ raised .*(ConnectionRefusedError|error)\((10061|111), .*refused.*\)""" def test_mock_builtin(): From b945cce11edfe856c718896e8814f2a03f6b10d6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ionel=20Cristian=20M=C4=83rie=C8=99?= Date: Wed, 18 Jun 2014 12:54:21 +0300 Subject: [PATCH 022/179] Use ellipses on diff header. --- docs/testing.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/testing.rst b/docs/testing.rst index 1cb4f7b..64b8ac4 100644 --- a/docs/testing.rst +++ b/docs/testing.rst @@ -144,7 +144,7 @@ Write an empty story and examine the output:: STORY/REPLAY DIFF: --- expected +++ actual - @@ -0,0 +1,8 @@ + @@ ... @@ +os.listdir('some') == ['test'] # returns +...isdir('some...test') == True # returns +os.listdir('some...test') == [...'empty'...] # returns From 355afa73428e8bda7f1d04beb6da4b9c2a020a8f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ionel=20Cristian=20M=C4=83rie=C8=99?= Date: Wed, 18 Jun 2014 12:56:10 +0300 Subject: [PATCH 023/179] Fix the test_mysql to be skipped if MySQLdb not importable. --- tests/test_integrations_py2.py | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/tests/test_integrations_py2.py b/tests/test_integrations_py2.py index 9255e24..1cc5fb2 100644 --- a/tests/test_integrations_py2.py +++ b/tests/test_integrations_py2.py @@ -8,6 +8,10 @@ import trollius as asyncio from tornado import gen from tornado import ioloop +try: + import MySQLdb +except ImportError: + MySQLdb = None from aspectlib import debug, weave, ALL_METHODS from aspectlib.test import Story @@ -42,7 +46,7 @@ def coro(): output = buf.getvalue() assert 'coro => %r' % 'result' in output -@pytest.mark.skipif(sys.platform.startswith('win'), reason="crappy windows") +@pytest.mark.skipif(not MySQLdb, reason="No MySQLdb installed") def test_mysql(): with Story(['MySQLdb.cursors.BaseCursor', 'MySQLdb.connections.Connection']) as story: pass From 9c94153c7dc8aab8002ce59e853ba9d75ddc8366 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ionel=20Cristian=20M=C4=83rie=C8=99?= Date: Wed, 18 Jun 2014 13:27:41 +0300 Subject: [PATCH 024/179] Update readme badges. --- README.rst | 38 +++++++++++++++++++++++--------------- 1 file changed, 23 insertions(+), 15 deletions(-) diff --git a/README.rst b/README.rst index 09f52f3..828f95a 100644 --- a/README.rst +++ b/README.rst @@ -2,21 +2,29 @@ python-aspectlib ================ -.. image:: http://img.shields.io/travis/ionelmc/python-aspectlib.png - :alt: Build Status - :target: https://travis-ci.org/ionelmc/python-aspectlib - -.. image:: http://img.shields.io/coveralls/ionelmc/python-aspectlib.png - :alt: Coverage Status - :target: https://coveralls.io/r/ionelmc/python-aspectlib - -.. image:: http://img.shields.io/pypi/v/aspectlib.png - :alt: PYPI Package - :target: https://pypi.python.org/pypi/aspectlib - -.. image:: http://img.shields.io/pypi/dm/aspectlib.png - :alt: PYPI Package - :target: https://pypi.python.org/pypi/aspectlib +.. list-table:: + :header-rows: 1 + + * - Build Status + - Coverage + - PyPI + * - .. image:: http://img.shields.io/travis/ionelmc/python-aspectlib.png + :alt: Travis-CI Build Status + :target: https://travis-ci.org/ionelmc/python-aspectlib + + .. image:: https://ci.appveyor.com/api/projects/status/u2f05p7rmd5hsixi + :alt: AppVeyor Build Status + :target: https://ci.appveyor.com/project/ionelmc/python-aspectlib + - .. image:: http://img.shields.io/coveralls/ionelmc/python-aspectlib.png + :alt: Coverage Status + :target: https://coveralls.io/r/ionelmc/python-aspectlib + - .. image:: http://img.shields.io/pypi/v/aspectlib.png + :alt: PYPI Package + :target: https://pypi.python.org/pypi/aspectlib + + .. image:: http://img.shields.io/pypi/dm/aspectlib.png + :alt: PYPI Package + :target: https://pypi.python.org/pypi/aspectlib ``aspectlib`` is an aspect-oriented programming, monkey-patch and decorators library. It is useful when changing behavior in existing code is desired. From f62417db0f9ed64b5d75855bda16940d38025db8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ionel=20Cristian=20M=C4=83rie=C8=99?= Date: Wed, 18 Jun 2014 13:30:23 +0300 Subject: [PATCH 025/179] Update README.rst --- README.rst | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/README.rst b/README.rst index 828f95a..1f94a31 100644 --- a/README.rst +++ b/README.rst @@ -3,11 +3,7 @@ python-aspectlib ================ .. list-table:: - :header-rows: 1 - - * - Build Status - - Coverage - - PyPI + * - .. image:: http://img.shields.io/travis/ionelmc/python-aspectlib.png :alt: Travis-CI Build Status :target: https://travis-ci.org/ionelmc/python-aspectlib From a0c5642d9a6dd31b4807ea62d953b78a2e41fd1e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ionel=20Cristian=20M=C4=83rie=C8=99?= Date: Wed, 18 Jun 2014 13:34:25 +0300 Subject: [PATCH 026/179] More ellipses. --- docs/testing.rst | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/docs/testing.rst b/docs/testing.rst index 64b8ac4..1781ad7 100644 --- a/docs/testing.rst +++ b/docs/testing.rst @@ -142,8 +142,8 @@ Write an empty story and examine the output:: │ └── file.txt └── empty STORY/REPLAY DIFF: - --- expected - +++ actual + --- expected... + +++ actual... @@ ... @@ +os.listdir('some') == ['test'] # returns +...isdir('some...test') == True # returns From 4aee6fc14d82b4ce0b48cd04efb265c1aa696299 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ionel=20Cristian=20M=C4=83rie=C8=99?= Date: Wed, 18 Jun 2014 13:36:12 +0300 Subject: [PATCH 027/179] Ooops. --- .travis.yml | 106 +++--- appveyor.yml | 52 +-- setup.cfg | 2 +- tox.ini | 1018 +++++++++++++++++++++++++------------------------- 4 files changed, 589 insertions(+), 589 deletions(-) diff --git a/.travis.yml b/.travis.yml index c968408..7e881c3 100644 --- a/.travis.yml +++ b/.travis.yml @@ -1,53 +1,53 @@ -language: python -python: 2.7 -env: - global: - LD_PRELOAD=/lib/x86_64-linux-gnu/libSegFault.so - matrix: - - TOXENV=check - - TOXENV=2.6,coveralls - - TOXENV=2.6-debug,coveralls - - TOXENV=2.6-nix,coveralls - - TOXENV=2.6-nix-debug,coveralls - - TOXENV=2.6-nix-nocover - - TOXENV=2.6-nix-nocover-debug - - TOXENV=2.6-nocover - - TOXENV=2.6-nocover-debug - - TOXENV=2.7,coveralls - - TOXENV=2.7-debug,coveralls - - TOXENV=2.7-nix,coveralls - - TOXENV=2.7-nix-debug,coveralls - - TOXENV=2.7-nix-nocover - - TOXENV=2.7-nix-nocover-debug - - TOXENV=2.7-nocover - - TOXENV=2.7-nocover-debug - - TOXENV=3.3,coveralls - - TOXENV=3.3-debug,coveralls - - TOXENV=3.3-nocover - - TOXENV=3.3-nocover-debug - - TOXENV=3.4,coveralls - - TOXENV=3.4-debug,coveralls - - TOXENV=3.4-nocover - - TOXENV=3.4-nocover-debug - - TOXENV=pypy,coveralls - - TOXENV=pypy-debug,coveralls - - TOXENV=pypy-nix,coveralls - - TOXENV=pypy-nix-debug,coveralls - - TOXENV=pypy-nix-nocover - - TOXENV=pypy-nix-nocover-debug - - TOXENV=pypy-nocover - - TOXENV=pypy-nocover-debug -before_install: - - python --version - - virtualenv --version - - pip --version - - uname -a - - lsb_release -a -install: - - pip install tox -script: - - tox -v -notifications: - email: - on_success: never - on_failure: always +language: python +python: 2.7 +env: + global: + LD_PRELOAD=/lib/x86_64-linux-gnu/libSegFault.so + matrix: + - TOXENV=check + - TOXENV=2.6,coveralls + - TOXENV=2.6-debug,coveralls + - TOXENV=2.6-nix,coveralls + - TOXENV=2.6-nix-debug,coveralls + - TOXENV=2.6-nix-nocover + - TOXENV=2.6-nix-nocover-debug + - TOXENV=2.6-nocover + - TOXENV=2.6-nocover-debug + - TOXENV=2.7,coveralls + - TOXENV=2.7-debug,coveralls + - TOXENV=2.7-nix,coveralls + - TOXENV=2.7-nix-debug,coveralls + - TOXENV=2.7-nix-nocover + - TOXENV=2.7-nix-nocover-debug + - TOXENV=2.7-nocover + - TOXENV=2.7-nocover-debug + - TOXENV=3.3,coveralls + - TOXENV=3.3-debug,coveralls + - TOXENV=3.3-nocover + - TOXENV=3.3-nocover-debug + - TOXENV=3.4,coveralls + - TOXENV=3.4-debug,coveralls + - TOXENV=3.4-nocover + - TOXENV=3.4-nocover-debug + - TOXENV=pypy,coveralls + - TOXENV=pypy-debug,coveralls + - TOXENV=pypy-nix,coveralls + - TOXENV=pypy-nix-debug,coveralls + - TOXENV=pypy-nix-nocover + - TOXENV=pypy-nix-nocover-debug + - TOXENV=pypy-nocover + - TOXENV=pypy-nocover-debug +before_install: + - python --version + - virtualenv --version + - pip --version + - uname -a + - lsb_release -a +install: + - pip install tox +script: + - tox -v +notifications: + email: + on_success: never + on_failure: always diff --git a/appveyor.yml b/appveyor.yml index c41cffd..051b82e 100644 --- a/appveyor.yml +++ b/appveyor.yml @@ -1,26 +1,26 @@ -build: false -environment: - matrix: - - TOXENV: check - - TOXENV: "2.7" - - TOXENV: "2.7-debug" - - TOXENV: "2.7-nocover" - - TOXENV: "2.7-nocover-debug" - - TOXENV: "3.4" - - TOXENV: "3.4-debug" - - TOXENV: "3.4-nocover" - - TOXENV: "3.4-nocover-debug" -init: - - "ECHO %TOXENV%" - - ps: "ls C:/Python*" -install: - - ps: Invoke-WebRequest "https://bitbucket.org/pypa/setuptools/raw/bootstrap/ez_setup.py" -OutFile "c:/ez_setup.py" - - ps: Invoke-WebRequest "https://bootstrap.pypa.io/get-pip.py" -OutFile "c:/get-pip.py" - - "c:/python27/python c:/ez_setup.py" - - "c:/python27/python c:/get-pip.py" - - "c:/python27/Scripts/pip install tox" -test_script: - - "c:/python27/Scripts/tox --version" - - "c:/python27/Scripts/virtualenv --version" - - "c:/python27/Scripts/pip --version" - - "c:/python27/Scripts/tox" +build: false +environment: + matrix: + - TOXENV: check + - TOXENV: "2.7" + - TOXENV: "2.7-debug" + - TOXENV: "2.7-nocover" + - TOXENV: "2.7-nocover-debug" + - TOXENV: "3.4" + - TOXENV: "3.4-debug" + - TOXENV: "3.4-nocover" + - TOXENV: "3.4-nocover-debug" +init: + - "ECHO %TOXENV%" + - ps: "ls C:/Python*" +install: + - ps: Invoke-WebRequest "https://bitbucket.org/pypa/setuptools/raw/bootstrap/ez_setup.py" -OutFile "c:/ez_setup.py" + - ps: Invoke-WebRequest "https://bootstrap.pypa.io/get-pip.py" -OutFile "c:/get-pip.py" + - "c:/python27/python c:/ez_setup.py" + - "c:/python27/python c:/get-pip.py" + - "c:/python27/Scripts/pip install tox" +test_script: + - "c:/python27/Scripts/tox --version" + - "c:/python27/Scripts/virtualenv --version" + - "c:/python27/Scripts/pip --version" + - "c:/python27/Scripts/tox" diff --git a/setup.cfg b/setup.cfg index e836055..df18309 100644 --- a/setup.cfg +++ b/setup.cfg @@ -30,7 +30,7 @@ python_versions = pypy dependencies = - nix: trollius-mysql: trollius !python_versions[3.*] + nix: trollius MySQL-python !python_versions[3.*] : trollius !python_versions[3.*] : asyncio &python_versions[3.3] : &python_versions[3.4] diff --git a/tox.ini b/tox.ini index 81b2c8a..c8577d2 100644 --- a/tox.ini +++ b/tox.ini @@ -1,509 +1,509 @@ -[tox] -envlist = - clean, - check, - 2.6, - 2.6-debug, - 2.6-nix, - 2.6-nix-debug, - 2.6-nix-nocover, - 2.6-nix-nocover-debug, - 2.6-nocover, - 2.6-nocover-debug, - 2.7, - 2.7-debug, - 2.7-nix, - 2.7-nix-debug, - 2.7-nix-nocover, - 2.7-nix-nocover-debug, - 2.7-nocover, - 2.7-nocover-debug, - 3.3, - 3.3-debug, - 3.3-nocover, - 3.3-nocover-debug, - 3.4, - 3.4-debug, - 3.4-nocover, - 3.4-nocover-debug, - pypy, - pypy-debug, - pypy-nix, - pypy-nix-debug, - pypy-nix-nocover, - pypy-nix-nocover-debug, - pypy-nocover, - pypy-nocover-debug, - report, - docs - -[testenv] -setenv = - PYTHONPATH={toxinidir}/tests - PYTHONUNBUFFERED=yes -deps = - pytest - pytest-capturelog - process-tests - nose - mock - tornado -commands = - {posargs:py.test -vv} - -[testenv:spell] -setenv = - SPELLCHECK = 1 -commands = - sphinx-build -b spelling docs dist/docs -usedevelop = true -deps = - -r{toxinidir}/docs/requirements.txt - sphinxcontrib-spelling - pyenchant - -[testenv:docs] -whitelist_externals = - rm -commands = - rm -rf dist/docs || rmdir /S /Q dist\docs - sphinx-build -b html docs dist/docs - sphinx-build -b linkcheck docs dist/docs -usedevelop = true -deps = - -r{toxinidir}/docs/requirements.txt - -[testenv:configure] -deps = - jinja2 - matrix -usedevelop = true -commands = - python configure - -[testenv:check] -basepython = python3.4 -deps = - docutils - check-manifest - flake8 -usedevelop = true -commands = - python setup.py check --restructuredtext --strict --metadata - check-manifest {toxinidir} - flake8 src - -[testenv:coveralls] -deps = - coveralls -usedevelop = true -commands = - coverage combine - coverage report - coveralls - -[testenv:report] -basepython = python3.4 -commands = - coverage combine - coverage report -usedevelop = true -deps = coverage - -[testenv:clean] -commands = coverage erase -usedevelop = true -deps = coverage - -[testenv:2.6] -basepython = python2.6 -setenv = - {[testenv]setenv} - WITH_COVERAGE=yes -usedevelop = true -commands = - {posargs:py.test --cov src -vv} -deps = - {[testenv]deps} - coverage - pytest-cov - trollius - -[testenv:2.6-debug] -basepython = python2.6 -setenv = - {[testenv]setenv} - ASPECTLIB_DEBUG=yes - WITH_COVERAGE=yes -usedevelop = true -commands = - {posargs:py.test --cov src -vv} -deps = - {[testenv]deps} - coverage - pytest-cov - trollius - -[testenv:2.6-nix] -basepython = python2.6 -setenv = - {[testenv]setenv} - WITH_COVERAGE=yes -usedevelop = true -commands = - {posargs:py.test --cov src -vv} -deps = - {[testenv]deps} - coverage - pytest-cov - trollius-mysql: - trollius - -[testenv:2.6-nix-debug] -basepython = python2.6 -setenv = - {[testenv]setenv} - ASPECTLIB_DEBUG=yes - WITH_COVERAGE=yes -usedevelop = true -commands = - {posargs:py.test --cov src -vv} -deps = - {[testenv]deps} - coverage - pytest-cov - trollius-mysql: - trollius - -[testenv:2.6-nix-nocover] -basepython = python2.6 -deps = - {[testenv]deps} - coverage - pytest-cov - trollius-mysql: - trollius - -[testenv:2.6-nix-nocover-debug] -basepython = python2.6 -setenv = - {[testenv]setenv} - ASPECTLIB_DEBUG=yes -deps = - {[testenv]deps} - coverage - pytest-cov - trollius-mysql: - trollius - -[testenv:2.6-nocover] -basepython = python2.6 -deps = - {[testenv]deps} - coverage - pytest-cov - trollius - -[testenv:2.6-nocover-debug] -basepython = python2.6 -setenv = - {[testenv]setenv} - ASPECTLIB_DEBUG=yes -deps = - {[testenv]deps} - coverage - pytest-cov - trollius - -[testenv:2.7] -basepython = python2.7 -setenv = - {[testenv]setenv} - WITH_COVERAGE=yes -usedevelop = true -commands = - {posargs:py.test --cov src -vv} -deps = - {[testenv]deps} - coverage - pytest-cov - trollius - -[testenv:2.7-debug] -basepython = python2.7 -setenv = - {[testenv]setenv} - ASPECTLIB_DEBUG=yes - WITH_COVERAGE=yes -usedevelop = true -commands = - {posargs:py.test --cov src -vv} -deps = - {[testenv]deps} - coverage - pytest-cov - trollius - -[testenv:2.7-nix] -basepython = python2.7 -setenv = - {[testenv]setenv} - WITH_COVERAGE=yes -usedevelop = true -commands = - {posargs:py.test --cov src -vv} -deps = - {[testenv]deps} - coverage - pytest-cov - trollius-mysql: - trollius - -[testenv:2.7-nix-debug] -basepython = python2.7 -setenv = - {[testenv]setenv} - ASPECTLIB_DEBUG=yes - WITH_COVERAGE=yes -usedevelop = true -commands = - {posargs:py.test --cov src -vv} -deps = - {[testenv]deps} - coverage - pytest-cov - trollius-mysql: - trollius - -[testenv:2.7-nix-nocover] -basepython = python2.7 -deps = - {[testenv]deps} - coverage - pytest-cov - trollius-mysql: - trollius - -[testenv:2.7-nix-nocover-debug] -basepython = python2.7 -setenv = - {[testenv]setenv} - ASPECTLIB_DEBUG=yes -deps = - {[testenv]deps} - coverage - pytest-cov - trollius-mysql: - trollius - -[testenv:2.7-nocover] -basepython = python2.7 -deps = - {[testenv]deps} - coverage - pytest-cov - trollius - -[testenv:2.7-nocover-debug] -basepython = python2.7 -setenv = - {[testenv]setenv} - ASPECTLIB_DEBUG=yes -deps = - {[testenv]deps} - coverage - pytest-cov - trollius - -[testenv:3.3] -basepython = python3.3 -setenv = - {[testenv]setenv} - WITH_COVERAGE=yes -usedevelop = true -commands = - {posargs:py.test --cov src -vv} -deps = - {[testenv]deps} - coverage - pytest-cov - asyncio - -[testenv:3.3-debug] -basepython = python3.3 -setenv = - {[testenv]setenv} - ASPECTLIB_DEBUG=yes - WITH_COVERAGE=yes -usedevelop = true -commands = - {posargs:py.test --cov src -vv} -deps = - {[testenv]deps} - coverage - pytest-cov - asyncio - -[testenv:3.3-nocover] -basepython = python3.3 -deps = - {[testenv]deps} - coverage - pytest-cov - asyncio - -[testenv:3.3-nocover-debug] -basepython = python3.3 -setenv = - {[testenv]setenv} - ASPECTLIB_DEBUG=yes -deps = - {[testenv]deps} - coverage - pytest-cov - asyncio - -[testenv:3.4] -basepython = python3.4 -setenv = - {[testenv]setenv} - WITH_COVERAGE=yes -usedevelop = true -commands = - {posargs:py.test --cov src -vv} -deps = - {[testenv]deps} - coverage - pytest-cov - -[testenv:3.4-debug] -basepython = python3.4 -setenv = - {[testenv]setenv} - ASPECTLIB_DEBUG=yes - WITH_COVERAGE=yes -usedevelop = true -commands = - {posargs:py.test --cov src -vv} -deps = - {[testenv]deps} - coverage - pytest-cov - -[testenv:3.4-nocover] -basepython = python3.4 -deps = - {[testenv]deps} - coverage - pytest-cov - -[testenv:3.4-nocover-debug] -basepython = python3.4 -setenv = - {[testenv]setenv} - ASPECTLIB_DEBUG=yes -deps = - {[testenv]deps} - coverage - pytest-cov - -[testenv:pypy] -basepython = pypy -setenv = - {[testenv]setenv} - WITH_COVERAGE=yes -usedevelop = true -commands = - {posargs:py.test --cov src -vv} -deps = - {[testenv]deps} - coverage - pytest-cov - trollius - -[testenv:pypy-debug] -basepython = pypy -setenv = - {[testenv]setenv} - ASPECTLIB_DEBUG=yes - WITH_COVERAGE=yes -usedevelop = true -commands = - {posargs:py.test --cov src -vv} -deps = - {[testenv]deps} - coverage - pytest-cov - trollius - -[testenv:pypy-nix] -basepython = pypy -setenv = - {[testenv]setenv} - WITH_COVERAGE=yes -usedevelop = true -commands = - {posargs:py.test --cov src -vv} -deps = - {[testenv]deps} - coverage - pytest-cov - trollius-mysql: - trollius - -[testenv:pypy-nix-debug] -basepython = pypy -setenv = - {[testenv]setenv} - ASPECTLIB_DEBUG=yes - WITH_COVERAGE=yes -usedevelop = true -commands = - {posargs:py.test --cov src -vv} -deps = - {[testenv]deps} - coverage - pytest-cov - trollius-mysql: - trollius - -[testenv:pypy-nix-nocover] -basepython = pypy -deps = - {[testenv]deps} - coverage - pytest-cov - trollius-mysql: - trollius - -[testenv:pypy-nix-nocover-debug] -basepython = pypy -setenv = - {[testenv]setenv} - ASPECTLIB_DEBUG=yes -deps = - {[testenv]deps} - coverage - pytest-cov - trollius-mysql: - trollius - -[testenv:pypy-nocover] -basepython = pypy -deps = - {[testenv]deps} - coverage - pytest-cov - trollius - -[testenv:pypy-nocover-debug] -basepython = pypy -setenv = - {[testenv]setenv} - ASPECTLIB_DEBUG=yes -deps = - {[testenv]deps} - coverage - pytest-cov - trollius - +[tox] +envlist = + clean, + check, + 2.6, + 2.6-debug, + 2.6-nix, + 2.6-nix-debug, + 2.6-nix-nocover, + 2.6-nix-nocover-debug, + 2.6-nocover, + 2.6-nocover-debug, + 2.7, + 2.7-debug, + 2.7-nix, + 2.7-nix-debug, + 2.7-nix-nocover, + 2.7-nix-nocover-debug, + 2.7-nocover, + 2.7-nocover-debug, + 3.3, + 3.3-debug, + 3.3-nocover, + 3.3-nocover-debug, + 3.4, + 3.4-debug, + 3.4-nocover, + 3.4-nocover-debug, + pypy, + pypy-debug, + pypy-nix, + pypy-nix-debug, + pypy-nix-nocover, + pypy-nix-nocover-debug, + pypy-nocover, + pypy-nocover-debug, + report, + docs + +[testenv] +setenv = + PYTHONPATH={toxinidir}/tests + PYTHONUNBUFFERED=yes +deps = + pytest + pytest-capturelog + process-tests + nose + mock + tornado +commands = + {posargs:py.test -vv} + +[testenv:spell] +setenv = + SPELLCHECK = 1 +commands = + sphinx-build -b spelling docs dist/docs +usedevelop = true +deps = + -r{toxinidir}/docs/requirements.txt + sphinxcontrib-spelling + pyenchant + +[testenv:docs] +whitelist_externals = + rm +commands = + rm -rf dist/docs || rmdir /S /Q dist\docs + sphinx-build -b html docs dist/docs + sphinx-build -b linkcheck docs dist/docs +usedevelop = true +deps = + -r{toxinidir}/docs/requirements.txt + +[testenv:configure] +deps = + jinja2 + matrix +usedevelop = true +commands = + python configure + +[testenv:check] +basepython = python3.4 +deps = + docutils + check-manifest + flake8 +usedevelop = true +commands = + python setup.py check --restructuredtext --strict --metadata + check-manifest {toxinidir} + flake8 src + +[testenv:coveralls] +deps = + coveralls +usedevelop = true +commands = + coverage combine + coverage report + coveralls + +[testenv:report] +basepython = python3.4 +commands = + coverage combine + coverage report +usedevelop = true +deps = coverage + +[testenv:clean] +commands = coverage erase +usedevelop = true +deps = coverage + +[testenv:2.6] +basepython = python2.6 +setenv = + {[testenv]setenv} + WITH_COVERAGE=yes +usedevelop = true +commands = + {posargs:py.test --cov src -vv} +deps = + {[testenv]deps} + coverage + pytest-cov + trollius + +[testenv:2.6-debug] +basepython = python2.6 +setenv = + {[testenv]setenv} + ASPECTLIB_DEBUG=yes + WITH_COVERAGE=yes +usedevelop = true +commands = + {posargs:py.test --cov src -vv} +deps = + {[testenv]deps} + coverage + pytest-cov + trollius + +[testenv:2.6-nix] +basepython = python2.6 +setenv = + {[testenv]setenv} + WITH_COVERAGE=yes +usedevelop = true +commands = + {posargs:py.test --cov src -vv} +deps = + {[testenv]deps} + coverage + pytest-cov + trollius + MySQL-python + +[testenv:2.6-nix-debug] +basepython = python2.6 +setenv = + {[testenv]setenv} + ASPECTLIB_DEBUG=yes + WITH_COVERAGE=yes +usedevelop = true +commands = + {posargs:py.test --cov src -vv} +deps = + {[testenv]deps} + coverage + pytest-cov + trollius + MySQL-python + +[testenv:2.6-nix-nocover] +basepython = python2.6 +deps = + {[testenv]deps} + coverage + pytest-cov + trollius + MySQL-python + +[testenv:2.6-nix-nocover-debug] +basepython = python2.6 +setenv = + {[testenv]setenv} + ASPECTLIB_DEBUG=yes +deps = + {[testenv]deps} + coverage + pytest-cov + trollius + MySQL-python + +[testenv:2.6-nocover] +basepython = python2.6 +deps = + {[testenv]deps} + coverage + pytest-cov + trollius + +[testenv:2.6-nocover-debug] +basepython = python2.6 +setenv = + {[testenv]setenv} + ASPECTLIB_DEBUG=yes +deps = + {[testenv]deps} + coverage + pytest-cov + trollius + +[testenv:2.7] +basepython = python2.7 +setenv = + {[testenv]setenv} + WITH_COVERAGE=yes +usedevelop = true +commands = + {posargs:py.test --cov src -vv} +deps = + {[testenv]deps} + coverage + pytest-cov + trollius + +[testenv:2.7-debug] +basepython = python2.7 +setenv = + {[testenv]setenv} + ASPECTLIB_DEBUG=yes + WITH_COVERAGE=yes +usedevelop = true +commands = + {posargs:py.test --cov src -vv} +deps = + {[testenv]deps} + coverage + pytest-cov + trollius + +[testenv:2.7-nix] +basepython = python2.7 +setenv = + {[testenv]setenv} + WITH_COVERAGE=yes +usedevelop = true +commands = + {posargs:py.test --cov src -vv} +deps = + {[testenv]deps} + coverage + pytest-cov + trollius + MySQL-python + +[testenv:2.7-nix-debug] +basepython = python2.7 +setenv = + {[testenv]setenv} + ASPECTLIB_DEBUG=yes + WITH_COVERAGE=yes +usedevelop = true +commands = + {posargs:py.test --cov src -vv} +deps = + {[testenv]deps} + coverage + pytest-cov + trollius + MySQL-python + +[testenv:2.7-nix-nocover] +basepython = python2.7 +deps = + {[testenv]deps} + coverage + pytest-cov + trollius + MySQL-python + +[testenv:2.7-nix-nocover-debug] +basepython = python2.7 +setenv = + {[testenv]setenv} + ASPECTLIB_DEBUG=yes +deps = + {[testenv]deps} + coverage + pytest-cov + trollius + MySQL-python + +[testenv:2.7-nocover] +basepython = python2.7 +deps = + {[testenv]deps} + coverage + pytest-cov + trollius + +[testenv:2.7-nocover-debug] +basepython = python2.7 +setenv = + {[testenv]setenv} + ASPECTLIB_DEBUG=yes +deps = + {[testenv]deps} + coverage + pytest-cov + trollius + +[testenv:3.3] +basepython = python3.3 +setenv = + {[testenv]setenv} + WITH_COVERAGE=yes +usedevelop = true +commands = + {posargs:py.test --cov src -vv} +deps = + {[testenv]deps} + coverage + pytest-cov + asyncio + +[testenv:3.3-debug] +basepython = python3.3 +setenv = + {[testenv]setenv} + ASPECTLIB_DEBUG=yes + WITH_COVERAGE=yes +usedevelop = true +commands = + {posargs:py.test --cov src -vv} +deps = + {[testenv]deps} + coverage + pytest-cov + asyncio + +[testenv:3.3-nocover] +basepython = python3.3 +deps = + {[testenv]deps} + coverage + pytest-cov + asyncio + +[testenv:3.3-nocover-debug] +basepython = python3.3 +setenv = + {[testenv]setenv} + ASPECTLIB_DEBUG=yes +deps = + {[testenv]deps} + coverage + pytest-cov + asyncio + +[testenv:3.4] +basepython = python3.4 +setenv = + {[testenv]setenv} + WITH_COVERAGE=yes +usedevelop = true +commands = + {posargs:py.test --cov src -vv} +deps = + {[testenv]deps} + coverage + pytest-cov + +[testenv:3.4-debug] +basepython = python3.4 +setenv = + {[testenv]setenv} + ASPECTLIB_DEBUG=yes + WITH_COVERAGE=yes +usedevelop = true +commands = + {posargs:py.test --cov src -vv} +deps = + {[testenv]deps} + coverage + pytest-cov + +[testenv:3.4-nocover] +basepython = python3.4 +deps = + {[testenv]deps} + coverage + pytest-cov + +[testenv:3.4-nocover-debug] +basepython = python3.4 +setenv = + {[testenv]setenv} + ASPECTLIB_DEBUG=yes +deps = + {[testenv]deps} + coverage + pytest-cov + +[testenv:pypy] +basepython = pypy +setenv = + {[testenv]setenv} + WITH_COVERAGE=yes +usedevelop = true +commands = + {posargs:py.test --cov src -vv} +deps = + {[testenv]deps} + coverage + pytest-cov + trollius + +[testenv:pypy-debug] +basepython = pypy +setenv = + {[testenv]setenv} + ASPECTLIB_DEBUG=yes + WITH_COVERAGE=yes +usedevelop = true +commands = + {posargs:py.test --cov src -vv} +deps = + {[testenv]deps} + coverage + pytest-cov + trollius + +[testenv:pypy-nix] +basepython = pypy +setenv = + {[testenv]setenv} + WITH_COVERAGE=yes +usedevelop = true +commands = + {posargs:py.test --cov src -vv} +deps = + {[testenv]deps} + coverage + pytest-cov + trollius + MySQL-python + +[testenv:pypy-nix-debug] +basepython = pypy +setenv = + {[testenv]setenv} + ASPECTLIB_DEBUG=yes + WITH_COVERAGE=yes +usedevelop = true +commands = + {posargs:py.test --cov src -vv} +deps = + {[testenv]deps} + coverage + pytest-cov + trollius + MySQL-python + +[testenv:pypy-nix-nocover] +basepython = pypy +deps = + {[testenv]deps} + coverage + pytest-cov + trollius + MySQL-python + +[testenv:pypy-nix-nocover-debug] +basepython = pypy +setenv = + {[testenv]setenv} + ASPECTLIB_DEBUG=yes +deps = + {[testenv]deps} + coverage + pytest-cov + trollius + MySQL-python + +[testenv:pypy-nocover] +basepython = pypy +deps = + {[testenv]deps} + coverage + pytest-cov + trollius + +[testenv:pypy-nocover-debug] +basepython = pypy +setenv = + {[testenv]setenv} + ASPECTLIB_DEBUG=yes +deps = + {[testenv]deps} + coverage + pytest-cov + trollius + From ba96b5b415f096f30ae1a3834b6ce5d759eb4572 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ionel=20Cristian=20M=C4=83rie=C8=99?= Date: Wed, 18 Jun 2014 13:46:11 +0300 Subject: [PATCH 028/179] Asciify this. --- docs/testing.rst | 28 ++++++++++++++-------------- 1 file changed, 14 insertions(+), 14 deletions(-) diff --git a/docs/testing.rst b/docs/testing.rst index 1781ad7..7a8ebea 100644 --- a/docs/testing.rst +++ b/docs/testing.rst @@ -79,10 +79,10 @@ Suppose we implement this simple GNU ``tree`` clone:: ... if not prefix: ... print("%s%s" % (prefix, os.path.basename(root))) ... for pos, name in reversed(list(enumerate(sorted(os.listdir(root), reverse=True)))): - ... print("%s%s%s" % (prefix, "├── " if pos else "└── ", name)) + ... print("%s%s%s" % (prefix, "|-- " if pos else "\-- ", name)) ... absname = os.path.join(root, name) ... if os.path.isdir(absname): - ... tree(absname, prefix + ("│ " if pos else " ")) + ... tree(absname, prefix + ("| " if pos else " ")) Lets suppose we would make up some directories and files for our tests:: @@ -95,10 +95,10 @@ And we'll assert that ``tree`` has this output:: >>> tree('some') some - └── test - ├── dir - │ └── file.txt - └── empty + \-- test + |-- dir + | \-- file.txt + \-- empty But now we're left with some garbage and have to clean it up:: @@ -137,10 +137,10 @@ Write an empty story and examine the output:: >>> with story.replay(strict=False) as replay: ... tree('some') some - └── test - ├── dir - │ └── file.txt - └── empty + \-- test + |-- dir + | \-- file.txt + \-- empty STORY/REPLAY DIFF: --- expected... +++ actual... @@ -191,10 +191,10 @@ Now we can remove the test directories and fill the story:: >>> with story.replay(proxy=False) as replay: ... tree('some') some - └── test - ├── dir - │ └── file.txt - └── empty + \-- test + |-- dir + | \-- file.txt + \-- empty >>> with story.replay(proxy=False, strict=False) as replay: ... tree('missing-from-story') From 39c88a818c0d3bc335210cdae98816ee39585e46 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ionel=20Cristian=20M=C4=83rie=C8=99?= Date: Wed, 18 Jun 2014 14:41:14 +0300 Subject: [PATCH 029/179] Move the templates in a templates dir. --- .travis.yml | 15 +- appveyor.yml | 8 +- configure | 11 +- .travis.tmpl.yml => templates/.travis.yml | 4 +- appveyor.tmpl.yml => templates/appveyor.yml | 0 tox.tmpl.ini => templates/tox.ini | 0 tox.ini | 144 ++++++++++---------- 7 files changed, 88 insertions(+), 94 deletions(-) rename .travis.tmpl.yml => templates/.travis.yml (80%) rename appveyor.tmpl.yml => templates/appveyor.yml (100%) rename tox.tmpl.ini => templates/tox.ini (100%) diff --git a/.travis.yml b/.travis.yml index 7e881c3..f35c75c 100644 --- a/.travis.yml +++ b/.travis.yml @@ -5,22 +5,16 @@ env: LD_PRELOAD=/lib/x86_64-linux-gnu/libSegFault.so matrix: - TOXENV=check - - TOXENV=2.6,coveralls - - TOXENV=2.6-debug,coveralls - TOXENV=2.6-nix,coveralls - TOXENV=2.6-nix-debug,coveralls - TOXENV=2.6-nix-nocover - TOXENV=2.6-nix-nocover-debug - - TOXENV=2.6-nocover - - TOXENV=2.6-nocover-debug - - TOXENV=2.7,coveralls - - TOXENV=2.7-debug,coveralls + - TOXENV=2.6-win,coveralls - TOXENV=2.7-nix,coveralls - TOXENV=2.7-nix-debug,coveralls - TOXENV=2.7-nix-nocover - TOXENV=2.7-nix-nocover-debug - - TOXENV=2.7-nocover - - TOXENV=2.7-nocover-debug + - TOXENV=2.7-win,coveralls - TOXENV=3.3,coveralls - TOXENV=3.3-debug,coveralls - TOXENV=3.3-nocover @@ -29,14 +23,11 @@ env: - TOXENV=3.4-debug,coveralls - TOXENV=3.4-nocover - TOXENV=3.4-nocover-debug - - TOXENV=pypy,coveralls - - TOXENV=pypy-debug,coveralls - TOXENV=pypy-nix,coveralls - TOXENV=pypy-nix-debug,coveralls - TOXENV=pypy-nix-nocover - TOXENV=pypy-nix-nocover-debug - - TOXENV=pypy-nocover - - TOXENV=pypy-nocover-debug + - TOXENV=pypy-win,coveralls before_install: - python --version - virtualenv --version diff --git a/appveyor.yml b/appveyor.yml index 051b82e..8a47ef5 100644 --- a/appveyor.yml +++ b/appveyor.yml @@ -2,10 +2,10 @@ build: false environment: matrix: - TOXENV: check - - TOXENV: "2.7" - - TOXENV: "2.7-debug" - - TOXENV: "2.7-nocover" - - TOXENV: "2.7-nocover-debug" + - TOXENV: "2.7-win" + - TOXENV: "2.7-win-debug" + - TOXENV: "2.7-win-nocover" + - TOXENV: "2.7-win-nocover-debug" - TOXENV: "3.4" - TOXENV: "3.4-debug" - TOXENV: "3.4-nocover" diff --git a/configure b/configure index 22c1742..9180d3e 100755 --- a/configure +++ b/configure @@ -8,11 +8,13 @@ fi .tox/configure/bin/python $0 $* exit ''' - +import glob +import os import jinja2 import matrix + jinja = jinja2.Environment( - loader=jinja2.FileSystemLoader('.'), + loader=jinja2.FileSystemLoader('templates'), trim_blocks=True, lstrip_blocks=True, keep_trailing_newline=True @@ -31,8 +33,9 @@ for alias, conf in matrix.from_file('setup.cfg').items(): 'env_vars': env_vars.split(), } -for name in ('tox.tmpl.ini', '.travis.tmpl.yml', 'appveyor.tmpl.yml'): - with open(name.replace('.tmpl', ''), 'w') as fh: +for name in os.listdir('templates'): + with open(name, 'w') as fh: fh.write(jinja.get_template(name).render(tox_environments=tox_environments)) + print("Wrote %s" % name) print("DONE.") diff --git a/.travis.tmpl.yml b/templates/.travis.yml similarity index 80% rename from .travis.tmpl.yml rename to templates/.travis.yml index b295333..02c64ed 100644 --- a/.travis.tmpl.yml +++ b/templates/.travis.yml @@ -5,10 +5,10 @@ env: LD_PRELOAD=/lib/x86_64-linux-gnu/libSegFault.so matrix: - TOXENV=check -{% for env, config in tox_environments|dictsort %} +{% for env, config in tox_environments|dictsort %}{% if '-win-' not in env %} - TOXENV={{ env }}{% if config.cover %},coveralls{% endif %} -{% endfor %} +{% endif %}{% endfor %} before_install: - python --version - virtualenv --version diff --git a/appveyor.tmpl.yml b/templates/appveyor.yml similarity index 100% rename from appveyor.tmpl.yml rename to templates/appveyor.yml diff --git a/tox.tmpl.ini b/templates/tox.ini similarity index 100% rename from tox.tmpl.ini rename to templates/tox.ini diff --git a/tox.ini b/tox.ini index c8577d2..f6b07b5 100644 --- a/tox.ini +++ b/tox.ini @@ -2,22 +2,22 @@ envlist = clean, check, - 2.6, - 2.6-debug, 2.6-nix, 2.6-nix-debug, 2.6-nix-nocover, 2.6-nix-nocover-debug, - 2.6-nocover, - 2.6-nocover-debug, - 2.7, - 2.7-debug, + 2.6-win, + 2.6-win-debug, + 2.6-win-nocover, + 2.6-win-nocover-debug, 2.7-nix, 2.7-nix-debug, 2.7-nix-nocover, 2.7-nix-nocover-debug, - 2.7-nocover, - 2.7-nocover-debug, + 2.7-win, + 2.7-win-debug, + 2.7-win-nocover, + 2.7-win-nocover-debug, 3.3, 3.3-debug, 3.3-nocover, @@ -26,14 +26,14 @@ envlist = 3.4-debug, 3.4-nocover, 3.4-nocover-debug, - pypy, - pypy-debug, pypy-nix, pypy-nix-debug, pypy-nix-nocover, pypy-nix-nocover-debug, - pypy-nocover, - pypy-nocover-debug, + pypy-win, + pypy-win-debug, + pypy-win-nocover, + pypy-win-nocover-debug, report, docs @@ -115,7 +115,7 @@ commands = coverage erase usedevelop = true deps = coverage -[testenv:2.6] +[testenv:2.6-nix] basepython = python2.6 setenv = {[testenv]setenv} @@ -128,8 +128,9 @@ deps = coverage pytest-cov trollius + MySQL-python -[testenv:2.6-debug] +[testenv:2.6-nix-debug] basepython = python2.6 setenv = {[testenv]setenv} @@ -143,15 +144,10 @@ deps = coverage pytest-cov trollius + MySQL-python -[testenv:2.6-nix] +[testenv:2.6-nix-nocover] basepython = python2.6 -setenv = - {[testenv]setenv} - WITH_COVERAGE=yes -usedevelop = true -commands = - {posargs:py.test --cov src -vv} deps = {[testenv]deps} coverage @@ -159,15 +155,11 @@ deps = trollius MySQL-python -[testenv:2.6-nix-debug] +[testenv:2.6-nix-nocover-debug] basepython = python2.6 setenv = {[testenv]setenv} ASPECTLIB_DEBUG=yes - WITH_COVERAGE=yes -usedevelop = true -commands = - {posargs:py.test --cov src -vv} deps = {[testenv]deps} coverage @@ -175,28 +167,36 @@ deps = trollius MySQL-python -[testenv:2.6-nix-nocover] +[testenv:2.6-win] basepython = python2.6 +setenv = + {[testenv]setenv} + WITH_COVERAGE=yes +usedevelop = true +commands = + {posargs:py.test --cov src -vv} deps = {[testenv]deps} coverage pytest-cov trollius - MySQL-python -[testenv:2.6-nix-nocover-debug] +[testenv:2.6-win-debug] basepython = python2.6 setenv = {[testenv]setenv} ASPECTLIB_DEBUG=yes + WITH_COVERAGE=yes +usedevelop = true +commands = + {posargs:py.test --cov src -vv} deps = {[testenv]deps} coverage pytest-cov trollius - MySQL-python -[testenv:2.6-nocover] +[testenv:2.6-win-nocover] basepython = python2.6 deps = {[testenv]deps} @@ -204,7 +204,7 @@ deps = pytest-cov trollius -[testenv:2.6-nocover-debug] +[testenv:2.6-win-nocover-debug] basepython = python2.6 setenv = {[testenv]setenv} @@ -215,7 +215,7 @@ deps = pytest-cov trollius -[testenv:2.7] +[testenv:2.7-nix] basepython = python2.7 setenv = {[testenv]setenv} @@ -228,8 +228,9 @@ deps = coverage pytest-cov trollius + MySQL-python -[testenv:2.7-debug] +[testenv:2.7-nix-debug] basepython = python2.7 setenv = {[testenv]setenv} @@ -243,15 +244,10 @@ deps = coverage pytest-cov trollius + MySQL-python -[testenv:2.7-nix] +[testenv:2.7-nix-nocover] basepython = python2.7 -setenv = - {[testenv]setenv} - WITH_COVERAGE=yes -usedevelop = true -commands = - {posargs:py.test --cov src -vv} deps = {[testenv]deps} coverage @@ -259,15 +255,11 @@ deps = trollius MySQL-python -[testenv:2.7-nix-debug] +[testenv:2.7-nix-nocover-debug] basepython = python2.7 setenv = {[testenv]setenv} ASPECTLIB_DEBUG=yes - WITH_COVERAGE=yes -usedevelop = true -commands = - {posargs:py.test --cov src -vv} deps = {[testenv]deps} coverage @@ -275,28 +267,36 @@ deps = trollius MySQL-python -[testenv:2.7-nix-nocover] +[testenv:2.7-win] basepython = python2.7 +setenv = + {[testenv]setenv} + WITH_COVERAGE=yes +usedevelop = true +commands = + {posargs:py.test --cov src -vv} deps = {[testenv]deps} coverage pytest-cov trollius - MySQL-python -[testenv:2.7-nix-nocover-debug] +[testenv:2.7-win-debug] basepython = python2.7 setenv = {[testenv]setenv} ASPECTLIB_DEBUG=yes + WITH_COVERAGE=yes +usedevelop = true +commands = + {posargs:py.test --cov src -vv} deps = {[testenv]deps} coverage pytest-cov trollius - MySQL-python -[testenv:2.7-nocover] +[testenv:2.7-win-nocover] basepython = python2.7 deps = {[testenv]deps} @@ -304,7 +304,7 @@ deps = pytest-cov trollius -[testenv:2.7-nocover-debug] +[testenv:2.7-win-nocover-debug] basepython = python2.7 setenv = {[testenv]setenv} @@ -407,7 +407,7 @@ deps = coverage pytest-cov -[testenv:pypy] +[testenv:pypy-nix] basepython = pypy setenv = {[testenv]setenv} @@ -420,8 +420,9 @@ deps = coverage pytest-cov trollius + MySQL-python -[testenv:pypy-debug] +[testenv:pypy-nix-debug] basepython = pypy setenv = {[testenv]setenv} @@ -435,15 +436,10 @@ deps = coverage pytest-cov trollius + MySQL-python -[testenv:pypy-nix] +[testenv:pypy-nix-nocover] basepython = pypy -setenv = - {[testenv]setenv} - WITH_COVERAGE=yes -usedevelop = true -commands = - {posargs:py.test --cov src -vv} deps = {[testenv]deps} coverage @@ -451,15 +447,11 @@ deps = trollius MySQL-python -[testenv:pypy-nix-debug] +[testenv:pypy-nix-nocover-debug] basepython = pypy setenv = {[testenv]setenv} ASPECTLIB_DEBUG=yes - WITH_COVERAGE=yes -usedevelop = true -commands = - {posargs:py.test --cov src -vv} deps = {[testenv]deps} coverage @@ -467,28 +459,36 @@ deps = trollius MySQL-python -[testenv:pypy-nix-nocover] +[testenv:pypy-win] basepython = pypy +setenv = + {[testenv]setenv} + WITH_COVERAGE=yes +usedevelop = true +commands = + {posargs:py.test --cov src -vv} deps = {[testenv]deps} coverage pytest-cov trollius - MySQL-python -[testenv:pypy-nix-nocover-debug] +[testenv:pypy-win-debug] basepython = pypy setenv = {[testenv]setenv} ASPECTLIB_DEBUG=yes + WITH_COVERAGE=yes +usedevelop = true +commands = + {posargs:py.test --cov src -vv} deps = {[testenv]deps} coverage pytest-cov trollius - MySQL-python -[testenv:pypy-nocover] +[testenv:pypy-win-nocover] basepython = pypy deps = {[testenv]deps} @@ -496,7 +496,7 @@ deps = pytest-cov trollius -[testenv:pypy-nocover-debug] +[testenv:pypy-win-nocover-debug] basepython = pypy setenv = {[testenv]setenv} From 2ab34f4b7ed5e9235b6ebecdc7bcbbc984d77dd8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ionel=20Cristian=20M=C4=83rie=C8=99?= Date: Wed, 18 Jun 2014 14:41:26 +0300 Subject: [PATCH 030/179] Use proper wheel section name. --- setup.cfg | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/setup.cfg b/setup.cfg index df18309..f3f89aa 100644 --- a/setup.cfg +++ b/setup.cfg @@ -1,4 +1,4 @@ -[wheel] +[bdist_wheel] universal = 1 [flake8] From 811b70629f55e768901e3ecce5668606544da576 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ionel=20Cristian=20M=C4=83rie=C8=99?= Date: Wed, 18 Jun 2014 14:41:49 +0300 Subject: [PATCH 031/179] Use a win alias. --- setup.cfg | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/setup.cfg b/setup.cfg index f3f89aa..26ee6cd 100644 --- a/setup.cfg +++ b/setup.cfg @@ -31,7 +31,7 @@ python_versions = dependencies = nix: trollius MySQL-python !python_versions[3.*] - : trollius !python_versions[3.*] + win: trollius !python_versions[3.*] : asyncio &python_versions[3.3] : &python_versions[3.4] From ae4c0f938dca5375d052b5478ef6e3e3876571aa Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ionel=20Cristian=20M=C4=83rie=C8=99?= Date: Wed, 18 Jun 2014 14:48:56 +0300 Subject: [PATCH 032/179] Use proper bootstrap url. --- appveyor.yml | 2 +- templates/appveyor.yml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/appveyor.yml b/appveyor.yml index 8a47ef5..e34904a 100644 --- a/appveyor.yml +++ b/appveyor.yml @@ -14,7 +14,7 @@ init: - "ECHO %TOXENV%" - ps: "ls C:/Python*" install: - - ps: Invoke-WebRequest "https://bitbucket.org/pypa/setuptools/raw/bootstrap/ez_setup.py" -OutFile "c:/ez_setup.py" + - ps: Invoke-WebRequest "https://bootstrap.pypa.io/ez_setup.py" -OutFile "c:/ez_setup.py" - ps: Invoke-WebRequest "https://bootstrap.pypa.io/get-pip.py" -OutFile "c:/get-pip.py" - "c:/python27/python c:/ez_setup.py" - "c:/python27/python c:/get-pip.py" diff --git a/templates/appveyor.yml b/templates/appveyor.yml index 6963e28..f059648 100644 --- a/templates/appveyor.yml +++ b/templates/appveyor.yml @@ -9,7 +9,7 @@ init: - "ECHO %TOXENV%" - ps: "ls C:/Python*" install: - - ps: Invoke-WebRequest "https://bitbucket.org/pypa/setuptools/raw/bootstrap/ez_setup.py" -OutFile "c:/ez_setup.py" + - ps: Invoke-WebRequest "https://bootstrap.pypa.io/ez_setup.py" -OutFile "c:/ez_setup.py" - ps: Invoke-WebRequest "https://bootstrap.pypa.io/get-pip.py" -OutFile "c:/get-pip.py" - "c:/python27/python c:/ez_setup.py" - "c:/python27/python c:/get-pip.py" From aead9bb99f739a947800c2eae9b71efe5fdea5d6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ionel=20Cristian=20M=C4=83rie=C8=99?= Date: Wed, 18 Jun 2014 15:17:31 +0300 Subject: [PATCH 033/179] Fix manifest. --- MANIFEST.in | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/MANIFEST.in b/MANIFEST.in index 4255b3c..96470a0 100644 --- a/MANIFEST.in +++ b/MANIFEST.in @@ -1,5 +1,6 @@ graft docs graft examples +graft templates graft src graft tests @@ -16,9 +17,7 @@ include LICENSE include README.rst include configure -include tox.ini tox.tmpl.ini -include .travis.yml .travis.tmpl.yml -include appveyor.tmpl.yml appveyor.yml +include tox.ini .travis.yml appveyor.yml include conftest.py global-exclude *.py[co] __pycache__ From 1cd258a6382aa9130a72fffdcd5f13e619172f9a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ionel=20Cristian=20M=C4=83rie=C8=99?= Date: Wed, 18 Jun 2014 17:01:45 +0300 Subject: [PATCH 034/179] Reorganize the header. --- README.rst | 39 ++++++++++++++++++++++----------------- 1 file changed, 22 insertions(+), 17 deletions(-) diff --git a/README.rst b/README.rst index 1f94a31..9159d05 100644 --- a/README.rst +++ b/README.rst @@ -4,23 +4,28 @@ python-aspectlib .. list-table:: - * - .. image:: http://img.shields.io/travis/ionelmc/python-aspectlib.png - :alt: Travis-CI Build Status - :target: https://travis-ci.org/ionelmc/python-aspectlib - - .. image:: https://ci.appveyor.com/api/projects/status/u2f05p7rmd5hsixi - :alt: AppVeyor Build Status - :target: https://ci.appveyor.com/project/ionelmc/python-aspectlib - - .. image:: http://img.shields.io/coveralls/ionelmc/python-aspectlib.png - :alt: Coverage Status - :target: https://coveralls.io/r/ionelmc/python-aspectlib - - .. image:: http://img.shields.io/pypi/v/aspectlib.png - :alt: PYPI Package - :target: https://pypi.python.org/pypi/aspectlib - - .. image:: http://img.shields.io/pypi/dm/aspectlib.png - :alt: PYPI Package - :target: https://pypi.python.org/pypi/aspectlib + * - | |travis-badge| + | |appveyor-badge| + - | |coverage-badge| + - | |version-badge| + | |downloads-badge| + +.. |travis-badge| image:: http://img.shields.io/travis/ionelmc/python-aspectlib.png + :alt: Travis-CI Build Status + :target: https://travis-ci.org/ionelmc/python-aspectlib +.. |appveyor-badge| image:: https://ci.appveyor.com/api/projects/status/u2f05p7rmd5hsixi + :alt: AppVeyor Build Status + :target: https://ci.appveyor.com/project/ionelmc/python-aspectlib +.. |coverage-badge| image:: http://img.shields.io/coveralls/ionelmc/python-aspectlib.png + :alt: Coverage Status + :target: https://coveralls.io/r/ionelmc/python-aspectlib +.. |version-badge| image:: http://img.shields.io/pypi/v/aspectlib.png + :alt: PYPI Package + :target: https://pypi.python.org/pypi/aspectlib +.. |downloads-badge| image:: http://img.shields.io/pypi/dm/aspectlib.png + :alt: PYPI Package + :target: https://pypi.python.org/pypi/aspectlib + ``aspectlib`` is an aspect-oriented programming, monkey-patch and decorators library. It is useful when changing behavior in existing code is desired. From a11a70f3952b0eea569bb6fd451b512d91aaff63 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ionel=20Cristian=20M=C4=83rie=C8=99?= Date: Wed, 18 Jun 2014 22:03:33 +0300 Subject: [PATCH 035/179] Use flat styles. --- README.rst | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/README.rst b/README.rst index 9159d05..f2d7c4d 100644 --- a/README.rst +++ b/README.rst @@ -10,19 +10,19 @@ python-aspectlib - | |version-badge| | |downloads-badge| -.. |travis-badge| image:: http://img.shields.io/travis/ionelmc/python-aspectlib.png +.. |travis-badge| image:: http://img.shields.io/travis/ionelmc/python-aspectlib.png?style=flat :alt: Travis-CI Build Status :target: https://travis-ci.org/ionelmc/python-aspectlib .. |appveyor-badge| image:: https://ci.appveyor.com/api/projects/status/u2f05p7rmd5hsixi :alt: AppVeyor Build Status :target: https://ci.appveyor.com/project/ionelmc/python-aspectlib -.. |coverage-badge| image:: http://img.shields.io/coveralls/ionelmc/python-aspectlib.png +.. |coverage-badge| image:: http://img.shields.io/coveralls/ionelmc/python-aspectlib.png?style=flat :alt: Coverage Status :target: https://coveralls.io/r/ionelmc/python-aspectlib -.. |version-badge| image:: http://img.shields.io/pypi/v/aspectlib.png +.. |version-badge| image:: http://img.shields.io/pypi/v/aspectlib.png?style=flat :alt: PYPI Package :target: https://pypi.python.org/pypi/aspectlib -.. |downloads-badge| image:: http://img.shields.io/pypi/dm/aspectlib.png +.. |downloads-badge| image:: http://img.shields.io/pypi/dm/aspectlib.png?style=flat :alt: PYPI Package :target: https://pypi.python.org/pypi/aspectlib From dad6abcfe5e261b64ac948b870a1c403d0b2bef0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ionel=20Cristian=20M=C4=83rie=C8=99?= Date: Fri, 20 Jun 2014 22:58:50 +0300 Subject: [PATCH 036/179] Remake table. --- README.rst | 12 +++++------- 1 file changed, 5 insertions(+), 7 deletions(-) diff --git a/README.rst b/README.rst index f2d7c4d..73080b1 100644 --- a/README.rst +++ b/README.rst @@ -2,13 +2,11 @@ python-aspectlib ================ -.. list-table:: - - * - | |travis-badge| - | |appveyor-badge| - - | |coverage-badge| - - | |version-badge| - | |downloads-badge| ++------------------+------------------+-------------------+ +| |travis-badge| | |coverage-badge| | |version-badge| | ++------------------+ +-------------------+ +| |appveyor-badge| | | |downloads-badge| | ++------------------+------------------+-------------------+ .. |travis-badge| image:: http://img.shields.io/travis/ionelmc/python-aspectlib.png?style=flat :alt: Travis-CI Build Status From 001aaec6f12944dcad2aeaf3783e15a77b51f34e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ionel=20Cristian=20M=C4=83rie=C8=99?= Date: Fri, 20 Jun 2014 23:05:59 +0300 Subject: [PATCH 037/179] Naa this is better. --- README.rst | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/README.rst b/README.rst index 73080b1..53218ca 100644 --- a/README.rst +++ b/README.rst @@ -2,11 +2,10 @@ python-aspectlib ================ -+------------------+------------------+-------------------+ -| |travis-badge| | |coverage-badge| | |version-badge| | -+------------------+ +-------------------+ -| |appveyor-badge| | | |downloads-badge| | -+------------------+------------------+-------------------+ ++--------------------+--------------------+---------------------+ +| | |travis-badge| | | | | |version-badge| | +| | |appveyor-badge| | | |coverage-badge| | | |downloads-badge| | ++--------------------+--------------------+---------------------+ .. |travis-badge| image:: http://img.shields.io/travis/ionelmc/python-aspectlib.png?style=flat :alt: Travis-CI Build Status From bcb942850434ae9921eea679185a9603aa08e3ed Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ionel=20Cristian=20M=C4=83rie=C8=99?= Date: Mon, 23 Jun 2014 21:48:10 +0300 Subject: [PATCH 038/179] Patch methods from baseclasses too. --- src/aspectlib/__init__.py | 17 ++++++++++++++++- src/aspectlib/test.py | 2 +- tests/test_aspectlib.py | 20 ++++++++++++++++++++ 3 files changed, 37 insertions(+), 2 deletions(-) diff --git a/src/aspectlib/__init__.py b/src/aspectlib/__init__.py index fee86b5..80fae48 100644 --- a/src/aspectlib/__init__.py +++ b/src/aspectlib/__init__.py @@ -571,10 +571,25 @@ def __init__(self, *args, **kwargs): else: continue original[attr] = func - entanglement.merge(lambda: deque(( setattr(klass, attr, func) for attr, func in original.items() ), maxlen=0)) + super_original = set() + for sklass in klass.__bases__: + if sklass is not object: + for attr, func in sklass.__dict__.items(): + if method_matches(attr) and attr not in original and attr not in super_original: + if isroutine(func): + logdebug("@ patching attribute %r (from superclass: %s, original: %r).", + attr, sklass.__name__, func) + setattr(klass, attr, _rewrap_method(func, klass, aspect)) + else: + continue + super_original.add(attr) + entanglement.merge(lambda: deque(( + delattr(klass, attr) for attr in super_original + ), maxlen=0)) + return entanglement diff --git a/src/aspectlib/test.py b/src/aspectlib/test.py index db8785e..bfee8e2 100644 --- a/src/aspectlib/test.py +++ b/src/aspectlib/test.py @@ -257,7 +257,7 @@ def __call__(self, *args, **kwargs): def __get__(self, binding, owner): return mimic(type(self)( - self._wrapped.__get__(binding, owner), + self._wrapped.__get__(binding, owner) if hasattr(self._wrapped, '__get__') else self._wrapped, handle=self._handle, binding=binding, owner=owner, diff --git a/tests/test_aspectlib.py b/tests/test_aspectlib.py index 9a66adc..c2a68af 100644 --- a/tests/test_aspectlib.py +++ b/tests/test_aspectlib.py @@ -61,6 +61,9 @@ def class_foobar(cls, foo, bar=None): def static_foobar(foo, bar=None): return bar or foo + def only_in_base(self): + return type(self).__name__ + class NormalTestSubClass(NormalTestClass): def foobar(self, foo, bar=None): @@ -547,6 +550,23 @@ def aspect(self): assert inst.foo == 'stuff' +def test_weave_subclass_meth_from_baseclass(): + history = [] + + @aspectlib.Aspect + def aspect(*args): + result = yield + history.append(args + (result,)) + yield aspectlib.Return('bar-'+result) + + with aspectlib.weave(NormalTestSubClass.only_in_base, aspect): + inst = NormalTestSubClass('stuff') + assert inst.only_in_base() == 'bar-NormalTestSubClass' + assert history == [ + (inst, 'NormalTestSubClass'), + ] + + def test_weave_class(): history = [] From ace85c67f9f6cbd2c58fa3592d814a54479c740c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ionel=20Cristian=20M=C4=83rie=C8=99?= Date: Mon, 23 Jun 2014 21:48:23 +0300 Subject: [PATCH 039/179] Make the logging look better. --- src/aspectlib/__init__.py | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/src/aspectlib/__init__.py b/src/aspectlib/__init__.py index 80fae48..aac4c15 100644 --- a/src/aspectlib/__init__.py +++ b/src/aspectlib/__init__.py @@ -292,7 +292,7 @@ def __exit__(self, *_): def _checked_apply(aspects, function, module=None): - logdebug(' > applying aspects %s to function %s.', aspects, function) + logdebug(' applying aspects %s to function %s.', aspects, function) if callable(aspects): wrapper = aspects(function) assert callable(wrapper), 'Aspect %s did not return a callable (it return %s).' % (aspects, wrapper) @@ -390,7 +390,7 @@ def weave(target, aspects, **options): while path: owner = getattr(owner, path.popleft()) - logdebug(" ~ patching %s from %s ...", name, owner) + logdebug("@ patching %s from %s ...", name, owner) obj = getattr(owner, name) if isinstance(obj, (type, ClassType)): @@ -410,7 +410,7 @@ def weave(target, aspects, **options): elif PY3 and ismethod(target): inst = target.__self__ name = target.__name__ - logdebug(" ~ patching %r (%s) as instance method.", target, name) + logdebug("@ patching %r (%s) as instance method.", target, name) assert not options, "keyword arguments are not supported when weaving instance methods." func = getattr(inst, name) setattr(inst, name, _checked_apply(aspects, func).__get__(inst, type(inst))) @@ -421,7 +421,7 @@ def weave(target, aspects, **options): while path: owner = getattr(owner, path.popleft()) name = target.__name__ - logdebug(" ~ patching %r (%s) as a property.", target, name) + logdebug("@ patching %r (%s) as a property.", target, name) func = owner.__dict__[name] return patch_module(owner, name, _checked_apply(aspects, func), func, **options) elif PY2 and isfunction(target): @@ -430,7 +430,7 @@ def weave(target, aspects, **options): if target.im_self: inst = target.im_self name = target.__name__ - logdebug(" ~ patching %r (%s) as instance method.", target, name) + logdebug("@ patching %r (%s) as instance method.", target, name) assert not options, "keyword arguments are not supported when weaving instance methods." func = getattr(inst, name) setattr(inst, name, _checked_apply(aspects, func).__get__(inst, type(inst))) @@ -566,7 +566,7 @@ def __init__(self, *args, **kwargs): for attr, func in klass.__dict__.items(): if method_matches(attr): if isroutine(func): - logdebug(" ~ patching attributes %r (original: %r).", attr, func) + logdebug("@ patching attribute %r (original: %r).", attr, func) setattr(klass, attr, _rewrap_method(func, klass, aspect)) else: continue @@ -620,14 +620,14 @@ def patch_module(module, name, replacement, original=UNSPECIFIED, aliases=True, obj = getattr(module, alias) if obj is original: if aliases or alias == name: - logdebug(" ~ saving %s on %s.%s ...", replacement, location, alias) + logdebug("= saving %s on %s.%s ...", replacement, location, alias) setattr(module, alias, replacement) rollback.merge(lambda alias=alias: setattr(module, alias, original)) if alias == name: seen = True elif alias == name: if ismethod(obj): - logdebug(" ~ saving %s on %s.%s ...", replacement, location, alias) + logdebug("= saving %s on %s.%s ...", replacement, location, alias) setattr(module, alias, replacement) rollback.merge(lambda alias=alias: setattr(module, alias, original)) else: @@ -637,7 +637,7 @@ def patch_module(module, name, replacement, original=UNSPECIFIED, aliases=True, warnings.warn('Setting %s.%s to %s. There was no previous definition, probably patching the wrong module.' % ( location, name, replacement )) - logdebug(" ~ saving %s on %s.%s ...", replacement, location, name) + logdebug("= saving %s on %s.%s ...", replacement, location, name) setattr(module, name, replacement) rollback.merge(lambda: setattr(module, name, original)) return rollback From e1d2a622c152d88dc4fa14ab63f44720f554fef1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ionel=20Cristian=20M=C4=83rie=C8=99?= Date: Mon, 23 Jun 2014 21:49:10 +0300 Subject: [PATCH 040/179] Fix the qualname in the recorder so it uses the patch target name (instead of the real platform-specific no-one-wants-to-see name). --- src/aspectlib/test.py | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/src/aspectlib/test.py b/src/aspectlib/test.py index bfee8e2..a65bacb 100644 --- a/src/aspectlib/test.py +++ b/src/aspectlib/test.py @@ -77,7 +77,6 @@ def __init__(self, wrapped, iscalled=True, calls=None, callback=None, extended=F assert not results or iscalled, "`iscalled` must be True if `results` is True" mimic(self, wrapped) self.__wrapped = wrapped - self.__name = qualname(wrapped) self.__entanglement = None self.__iscalled = iscalled self.__binding = binding @@ -112,11 +111,11 @@ def __call__(self, *args, **kwargs): def __record(self, args, kwargs, *response): if self.__callback is not None: - self.__callback(self.__binding, self.__name, args, kwargs, *response) + self.__callback(self.__binding, qualname(self), args, kwargs, *response) if self.calls is not None: if self.__extended: self.calls.append((ResultEx if response else CallEx)( - self.__binding, self.__name, args, kwargs, *response + self.__binding, qualname(self), args, kwargs, *response )) else: self.calls.append((Result if response else Call)( From 82b71f6e70c23d0eafda704bc01c54ed6e51b5ee Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ionel=20Cristian=20M=C4=83rie=C8=99?= Date: Tue, 24 Jun 2014 02:53:39 +0300 Subject: [PATCH 041/179] Fix weaving functions so you can't weave same thing multiple times (by proxy of automatic subclass weaving or plain wrong usage). --- src/aspectlib/__init__.py | 131 +++++++++++++++++++++++++++----------- 1 file changed, 93 insertions(+), 38 deletions(-) diff --git a/src/aspectlib/__init__.py b/src/aspectlib/__init__.py index aac4c15..2c724f4 100644 --- a/src/aspectlib/__init__.py +++ b/src/aspectlib/__init__.py @@ -291,6 +291,33 @@ def __exit__(self, *_): rollback = __call__ = __exit__ +class ObjectBag(object): + def __init__(self): + self._objects = {} + + def has(self, obj): + if id(obj) in self._objects: + logdebug(' --- ObjectBag ALREADY HAS %r', obj) + return True + else: + self._objects[id(obj)] = obj + return False + +BrokenBag = type('BrokenBag', (), dict(has=lambda self, obj: False))() + + +class EmptyRollback(object): + def __enter__(self): + return self + + def __exit__(self, *_): + pass + + rollback = __call__ = __exit__ + +Nothing = EmptyRollback() + + def _checked_apply(aspects, function, module=None): logdebug(' applying aspects %s to function %s.', aspects, function) if callable(aspects): @@ -359,6 +386,9 @@ def weave(target, aspects, **options): raise ExpectedAdvice('%s must be an `Aspect` instance or a callable.' % obj) assert target, "Can't weave falsy value %r." % target logdebug("weave (target=%s, aspects=%s, **options=%s)", target, aspects, options) + + bag = options.setdefault('bag', ObjectBag()) + if isinstance(target, (list, tuple)): return Rollback([ weave(item, aspects, **options) for item in target @@ -401,13 +431,20 @@ def weave(target, aspects, **options): ) elif callable(obj): # or isinstance(obj, FunctionType) ?? logdebug(" .. as a callable %r.", obj) - return weave_module_function(owner, obj, aspects, force_name=name, **options) + if bag.has(obj): + return Nothing + return patch_module_function(owner, obj, aspects, force_name=name, **options) else: return weave(obj, aspects, **options) + name = getattr(target, '__name__', None) if name and getattr(__builtin__, name, None) is target: - return weave_module_function(__builtin__, target, aspects, **options) + if bag.has(target): + return Nothing + return patch_module_function(__builtin__, target, aspects, **options) elif PY3 and ismethod(target): + if bag.has(target): + return Nothing inst = target.__self__ name = target.__name__ logdebug("@ patching %r (%s) as instance method.", target, name) @@ -416,6 +453,8 @@ def weave(target, aspects, **options): setattr(inst, name, _checked_apply(aspects, func).__get__(inst, type(inst))) return Rollback(lambda: delattr(inst, name)) elif PY3 and isfunction(target): + if bag.has(target): + return Nothing owner = __import__(target.__module__) path = deque(target.__qualname__.split('.')[:-1]) while path: @@ -425,9 +464,13 @@ def weave(target, aspects, **options): func = owner.__dict__[name] return patch_module(owner, name, _checked_apply(aspects, func), func, **options) elif PY2 and isfunction(target): - return weave_module_function(__import__(target.__module__), target, aspects, **options) + if bag.has(target): + return Nothing + return patch_module_function(__import__(target.__module__), target, aspects, **options) elif PY2 and ismethod(target): if target.im_self: + if bag.has(target): + return Nothing inst = target.im_self name = target.__name__ logdebug("@ patching %r (%s) as instance method.", target, name) @@ -464,14 +507,17 @@ def _rewrap_method(func, klass, aspect): return _checked_apply(aspect, func) -def weave_instance(instance, aspect, methods=NORMAL_METHODS, lazy=False, **options): +def weave_instance(instance, aspect, methods=NORMAL_METHODS, lazy=False, bag=BrokenBag, **options): """ Low-level weaver for instances. .. warning:: You should not use this directly. - :returns: An :obj:`aspectlib.Entanglement` object. + :returns: An :obj:`aspectlib.Rollback` object. """ + if bag.has(instance): + return Nothing + entanglement = Rollback() method_matches = make_method_matcher(methods) logdebug("weave_instance (module=%r, aspect=%s, methods=%s, lazy=%s, **options=%s)", @@ -493,14 +539,17 @@ def weave_instance(instance, aspect, methods=NORMAL_METHODS, lazy=False, **optio return entanglement -def weave_module(module, aspect, methods=NORMAL_METHODS, lazy=False, **options): +def weave_module(module, aspect, methods=NORMAL_METHODS, lazy=False, bag=BrokenBag, **options): """ Low-level weaver for "whole module weaving". .. warning:: You should not use this directly. - :returns: An :obj:`aspectlib.Entanglement` object. + :returns: An :obj:`aspectlib.Rollback` object. """ + if bag.has(module): + return Nothing + entanglement = Rollback() method_matches = make_method_matcher(methods) logdebug("weave_module (module=%r, aspect=%s, methods=%s, lazy=%s, **options=%s)", @@ -510,35 +559,42 @@ def weave_module(module, aspect, methods=NORMAL_METHODS, lazy=False, **options): func = getattr(module, attr) if method_matches(attr): if isroutine(func): - entanglement.merge(weave_module_function(module, func, aspect, force_name=attr, **options)) + entanglement.merge(patch_module_function(module, func, aspect, force_name=attr, **options)) elif isclass(func): entanglement.merge( - weave_class(func, aspect, owner=module, name=attr, methods=methods, lazy=lazy, **options), + weave_class(func, aspect, owner=module, name=attr, methods=methods, lazy=lazy, bag=bag, **options), # it's not consistent with the other ways of weaving a class (it's never weaved as a routine). # therefore it's disabled until it's considered useful. - # #weave_module_function(module, getattr(module, attr), aspect, force_name=attr, **options), + # #patch_module_function(module, getattr(module, attr), aspect, force_name=attr, **options), ) return entanglement def weave_class(klass, aspect, methods=NORMAL_METHODS, subclasses=True, lazy=False, - owner=None, name=None, aliases=True): + owner=None, name=None, aliases=True, bases=True, bag=BrokenBag): """ Low-level weaver for classes. .. warning:: You should not use this directly. """ - assert isclass(klass), "Can't weave %r. Must be a class." % klass + + if bag.has(klass): + return Nothing + entanglement = Rollback() method_matches = make_method_matcher(methods) + logdebug("weave_class (klass=%r, methods=%s, subclasses=%s, lazy=%s, owner=%s, name=%s, aliases=%s, bases=%s)", + klass, methods, subclasses, lazy, owner, name, aliases, bases) if subclasses and hasattr(klass, '__subclasses__'): - for sub_class in klass.__subclasses__(): + sub_targets = klass.__subclasses__() + if sub_targets: + logdebug("~ weaving subclasses: %s", sub_targets) + for sub_class in sub_targets: if not issubclass(sub_class, Fabric): - entanglement.merge(weave_class(sub_class, aspect, methods=methods, subclasses=subclasses, lazy=lazy)) - logdebug("weave_class (klass=%r, methods=%s, subclasses=%s, lazy=%s, owner=%s, name=%s, aliases=%s)", - klass, methods, subclasses, lazy, owner, name, aliases) + entanglement.merge(weave_class(sub_class, aspect, + methods=methods, subclasses=subclasses, lazy=lazy, bases=False, bag=bag)) if lazy: def __init__(self, *args, **kwargs): super(SubClass, self).__init__(*args, **kwargs) @@ -574,22 +630,22 @@ def __init__(self, *args, **kwargs): entanglement.merge(lambda: deque(( setattr(klass, attr, func) for attr, func in original.items() ), maxlen=0)) - super_original = set() - for sklass in klass.__bases__: - if sklass is not object: - for attr, func in sklass.__dict__.items(): - if method_matches(attr) and attr not in original and attr not in super_original: - if isroutine(func): - logdebug("@ patching attribute %r (from superclass: %s, original: %r).", - attr, sklass.__name__, func) - setattr(klass, attr, _rewrap_method(func, klass, aspect)) - else: - continue - super_original.add(attr) - entanglement.merge(lambda: deque(( - delattr(klass, attr) for attr in super_original - ), maxlen=0)) - + if bases: + super_original = set() + for sklass in klass.__bases__: + if sklass is not object: + for attr, func in sklass.__dict__.items(): + if method_matches(attr) and attr not in original and attr not in super_original: + if isroutine(func): + logdebug("@ patching attribute %r (from superclass: %s, original: %r).", + attr, sklass.__name__, func) + setattr(klass, attr, _rewrap_method(func, sklass, aspect)) + else: + continue + super_original.add(attr) + entanglement.merge(lambda: deque(( + delattr(klass, attr) for attr in super_original + ), maxlen=0)) return entanglement @@ -604,9 +660,8 @@ def patch_module(module, name, replacement, original=UNSPECIFIED, aliases=True, :param original: The original value (in case the object beeing patched uses descriptors or is plain weird). :param bool aliases: If ``True`` patch all the attributes that have the same original value. - :returns: An :obj:`aspectlib.Entanglement` object. + :returns: An :obj:`aspectlib.Rollback` object. """ - rollback = Rollback() seen = False original = getattr(module, name) if original is UNSPECIFIED else original @@ -643,15 +698,15 @@ def patch_module(module, name, replacement, original=UNSPECIFIED, aliases=True, return rollback -def weave_module_function(module, target, aspect, force_name=None, **options): +def patch_module_function(module, target, aspect, force_name=None, bag=BrokenBag, **options): """ - Low-level weaver for one function from a specified module. + Low-level patcher for one function from a specified module. .. warning:: You should not use this directly. - :returns: An :obj:`aspectlib.Entanglement` object. + :returns: An :obj:`aspectlib.Rollback` object. """ - logdebug("weave_module_function (module=%s, target=%s, aspect=%s, force_name=%s, **options=%s", + logdebug("patch_module_function (module=%s, target=%s, aspect=%s, force_name=%s, **options=%s", module, target, aspect, force_name, options) name = force_name or target.__name__ return patch_module(module, name, _checked_apply(aspect, target, module=module), original=target, **options) From 51d60a0b4d47edf8f912cd5894a92f62d9046881 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ionel=20Cristian=20M=C4=83rie=C8=99?= Date: Tue, 24 Jun 2014 02:54:02 +0300 Subject: [PATCH 042/179] Add some more assertions. --- tests/test_aspectlib.py | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/tests/test_aspectlib.py b/tests/test_aspectlib.py index c2a68af..2d8ee80 100644 --- a/tests/test_aspectlib.py +++ b/tests/test_aspectlib.py @@ -566,6 +566,10 @@ def aspect(*args): (inst, 'NormalTestSubClass'), ] + inst = NormalTestSubClass('stuff') + assert inst.only_in_base() == 'NormalTestSubClass' + + def test_weave_class(): history = [] From 6ea5cf65818bbd6ddb701a30d0ee3a7b8b68e28e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ionel=20Cristian=20M=C4=83rie=C8=99?= Date: Tue, 24 Jun 2014 04:12:26 +0300 Subject: [PATCH 043/179] Disable assertions - they are actually useless. --- src/aspectlib/__init__.py | 2 -- 1 file changed, 2 deletions(-) diff --git a/src/aspectlib/__init__.py b/src/aspectlib/__init__.py index 2c724f4..fcf11f3 100644 --- a/src/aspectlib/__init__.py +++ b/src/aspectlib/__init__.py @@ -448,7 +448,6 @@ def weave(target, aspects, **options): inst = target.__self__ name = target.__name__ logdebug("@ patching %r (%s) as instance method.", target, name) - assert not options, "keyword arguments are not supported when weaving instance methods." func = getattr(inst, name) setattr(inst, name, _checked_apply(aspects, func).__get__(inst, type(inst))) return Rollback(lambda: delattr(inst, name)) @@ -474,7 +473,6 @@ def weave(target, aspects, **options): inst = target.im_self name = target.__name__ logdebug("@ patching %r (%s) as instance method.", target, name) - assert not options, "keyword arguments are not supported when weaving instance methods." func = getattr(inst, name) setattr(inst, name, _checked_apply(aspects, func).__get__(inst, type(inst))) return Rollback(lambda: delattr(inst, name)) From 61960f061fed87ba311c786b40fb77df3f3a7ff1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ionel=20Cristian=20M=C4=83rie=C8=99?= Date: Tue, 24 Jun 2014 04:47:41 +0300 Subject: [PATCH 044/179] Disable the mysql stuff. Update skel. --- .travis.yml | 27 ++-- appveyor.yml | 8 +- bootstrap.py | 46 +++++++ {templates => conf}/.travis.yml | 4 +- {templates => conf}/appveyor.yml | 2 +- {templates => conf}/tox.ini | 2 +- configure | 41 ------ setup.cfg | 3 +- setup.py | 19 ++- tox.ini | 218 ++++--------------------------- 10 files changed, 104 insertions(+), 266 deletions(-) create mode 100755 bootstrap.py rename {templates => conf}/.travis.yml (80%) rename {templates => conf}/appveyor.yml (85%) rename {templates => conf}/tox.ini (98%) delete mode 100755 configure diff --git a/.travis.yml b/.travis.yml index f35c75c..2f2dd13 100644 --- a/.travis.yml +++ b/.travis.yml @@ -5,16 +5,14 @@ env: LD_PRELOAD=/lib/x86_64-linux-gnu/libSegFault.so matrix: - TOXENV=check - - TOXENV=2.6-nix,coveralls - - TOXENV=2.6-nix-debug,coveralls - - TOXENV=2.6-nix-nocover - - TOXENV=2.6-nix-nocover-debug - - TOXENV=2.6-win,coveralls - - TOXENV=2.7-nix,coveralls - - TOXENV=2.7-nix-debug,coveralls - - TOXENV=2.7-nix-nocover - - TOXENV=2.7-nix-nocover-debug - - TOXENV=2.7-win,coveralls + - TOXENV=2.6,coveralls + - TOXENV=2.6-debug,coveralls + - TOXENV=2.6-nocover + - TOXENV=2.6-nocover-debug + - TOXENV=2.7,coveralls + - TOXENV=2.7-debug,coveralls + - TOXENV=2.7-nocover + - TOXENV=2.7-nocover-debug - TOXENV=3.3,coveralls - TOXENV=3.3-debug,coveralls - TOXENV=3.3-nocover @@ -23,11 +21,10 @@ env: - TOXENV=3.4-debug,coveralls - TOXENV=3.4-nocover - TOXENV=3.4-nocover-debug - - TOXENV=pypy-nix,coveralls - - TOXENV=pypy-nix-debug,coveralls - - TOXENV=pypy-nix-nocover - - TOXENV=pypy-nix-nocover-debug - - TOXENV=pypy-win,coveralls + - TOXENV=pypy,coveralls + - TOXENV=pypy-debug,coveralls + - TOXENV=pypy-nocover + - TOXENV=pypy-nocover-debug before_install: - python --version - virtualenv --version diff --git a/appveyor.yml b/appveyor.yml index e34904a..91405dd 100644 --- a/appveyor.yml +++ b/appveyor.yml @@ -2,10 +2,10 @@ build: false environment: matrix: - TOXENV: check - - TOXENV: "2.7-win" - - TOXENV: "2.7-win-debug" - - TOXENV: "2.7-win-nocover" - - TOXENV: "2.7-win-nocover-debug" + - TOXENV: "2.7" + - TOXENV: "2.7-debug" + - TOXENV: "2.7-nocover" + - TOXENV: "2.7-nocover-debug" - TOXENV: "3.4" - TOXENV: "3.4-debug" - TOXENV: "3.4-nocover" diff --git a/bootstrap.py b/bootstrap.py new file mode 100755 index 0000000..e8fe968 --- /dev/null +++ b/bootstrap.py @@ -0,0 +1,46 @@ +#!/usr/bin/env python +import os +import sys +if not os.path.exists('.tox/configure'): + import virtualenv + import subprocess + print("Bootstrapping ...") + virtualenv.create_environment('.tox/configure') + print("Installing `jinja2` and `matrix` into bootstrap environment ...") + if sys.platform == 'win32': + subprocess.check_call(['.tox/configure/Scripts/pip', 'install', 'jinja2', 'matrix']) + else: + subprocess.check_call(['.tox/configure/bin/pip', 'install', 'jinja2', 'matrix']) +if sys.platform == 'win32': + execfile('.tox/configure/Scripts/activate_this.py', dict(__file__='.tox/configure/Scripts/activate_this.py')) +else: + execfile('.tox/configure/bin/activate_this.py', dict(__file__='.tox/configure/bin/activate_this.py')) +import jinja2 +import matrix + +jinja = jinja2.Environment( + loader=jinja2.FileSystemLoader('conf'), + trim_blocks=True, + lstrip_blocks=True, + keep_trailing_newline=True +) +tox_environments = {} +for alias, conf in matrix.from_file('setup.cfg').items(): + python = conf['python_versions'] + deps = conf['dependencies'] + cover = {'false': False, 'true': True}[conf['coverage_flags'].lower()] + env_vars = conf['environment_variables'] + + tox_environments[alias] = { + 'python': 'python' + python if 'py' not in python else python, + 'deps': deps.split(), + 'cover': cover, + 'env_vars': env_vars.split(), + } + +for name in os.listdir('conf'): + with open(name, 'w') as fh: + fh.write(jinja.get_template(name).render(tox_environments=tox_environments)) + print("Wrote %s" % name) + +print("DONE.") \ No newline at end of file diff --git a/templates/.travis.yml b/conf/.travis.yml similarity index 80% rename from templates/.travis.yml rename to conf/.travis.yml index 02c64ed..b295333 100644 --- a/templates/.travis.yml +++ b/conf/.travis.yml @@ -5,10 +5,10 @@ env: LD_PRELOAD=/lib/x86_64-linux-gnu/libSegFault.so matrix: - TOXENV=check -{% for env, config in tox_environments|dictsort %}{% if '-win-' not in env %} +{% for env, config in tox_environments|dictsort %} - TOXENV={{ env }}{% if config.cover %},coveralls{% endif %} -{% endif %}{% endfor %} +{% endfor %} before_install: - python --version - virtualenv --version diff --git a/templates/appveyor.yml b/conf/appveyor.yml similarity index 85% rename from templates/appveyor.yml rename to conf/appveyor.yml index f059648..e5c93a2 100644 --- a/templates/appveyor.yml +++ b/conf/appveyor.yml @@ -2,7 +2,7 @@ build: false environment: matrix: - TOXENV: check -{% for env, config in tox_environments|dictsort %}{% if ('2.7' in env or '3.4' in env) and 'nix' not in env %} +{% for env, config in tox_environments|dictsort %}{% if '2.7' in env or '3.4' in env %} - TOXENV: "{{ env }}" {% endif %}{% endfor %} init: diff --git a/templates/tox.ini b/conf/tox.ini similarity index 98% rename from templates/tox.ini rename to conf/tox.ini index 8b0902e..67475dd 100644 --- a/templates/tox.ini +++ b/conf/tox.ini @@ -50,7 +50,7 @@ deps = matrix usedevelop = true commands = - python configure + python bootstrap.py [testenv:check] basepython = python3.4 diff --git a/configure b/configure deleted file mode 100755 index 9180d3e..0000000 --- a/configure +++ /dev/null @@ -1,41 +0,0 @@ -#!/bin/sh -e -# this a self-bootstrapping script, it will create a virtualenv and install jinja2 in it -bogus=''' ' -if [ ! -e .tox/configure ]; then - virtualenv .tox/configure - .tox/configure/bin/pip install jinja2 matrix -fi -.tox/configure/bin/python $0 $* -exit -''' -import glob -import os -import jinja2 -import matrix - -jinja = jinja2.Environment( - loader=jinja2.FileSystemLoader('templates'), - trim_blocks=True, - lstrip_blocks=True, - keep_trailing_newline=True -) -tox_environments = {} -for alias, conf in matrix.from_file('setup.cfg').items(): - python = conf['python_versions'] - deps = conf['dependencies'] - cover = {'false': False, 'true': True}[conf['coverage_flags'].lower()] - env_vars = conf['environment_variables'] - - tox_environments[alias] = { - 'python': 'python' + python if 'py' not in python else python, - 'deps': deps.split(), - 'cover': cover, - 'env_vars': env_vars.split(), - } - -for name in os.listdir('templates'): - with open(name, 'w') as fh: - fh.write(jinja.get_template(name).render(tox_environments=tox_environments)) - print("Wrote %s" % name) - -print("DONE.") diff --git a/setup.cfg b/setup.cfg index 26ee6cd..6a3304a 100644 --- a/setup.cfg +++ b/setup.cfg @@ -30,8 +30,7 @@ python_versions = pypy dependencies = - nix: trollius MySQL-python !python_versions[3.*] - win: trollius !python_versions[3.*] + : trollius !python_versions[3.*] : asyncio &python_versions[3.3] : &python_versions[3.4] diff --git a/setup.py b/setup.py index a35be2c..3a59fe2 100644 --- a/setup.py +++ b/setup.py @@ -1,15 +1,20 @@ -# -*- encoding: utf-8 -*- -from setuptools import setup, find_packages - -import os -import re +# -*- encoding: utf8 -*- +import glob import io +import re +from os.path import basename +from os.path import dirname +from os.path import join +from os.path import splitext + +from setuptools import find_packages +from setuptools import setup def read(*names, **kwargs): return io.open( - os.path.join(os.path.dirname(__file__), *names), - encoding=kwargs.get('encoding', 'utf8') + join(dirname(__file__), *names), + encoding=kwargs.get("encoding", "utf8") ).read() setup( diff --git a/tox.ini b/tox.ini index f6b07b5..6f78ea1 100644 --- a/tox.ini +++ b/tox.ini @@ -2,22 +2,14 @@ envlist = clean, check, - 2.6-nix, - 2.6-nix-debug, - 2.6-nix-nocover, - 2.6-nix-nocover-debug, - 2.6-win, - 2.6-win-debug, - 2.6-win-nocover, - 2.6-win-nocover-debug, - 2.7-nix, - 2.7-nix-debug, - 2.7-nix-nocover, - 2.7-nix-nocover-debug, - 2.7-win, - 2.7-win-debug, - 2.7-win-nocover, - 2.7-win-nocover-debug, + 2.6, + 2.6-debug, + 2.6-nocover, + 2.6-nocover-debug, + 2.7, + 2.7-debug, + 2.7-nocover, + 2.7-nocover-debug, 3.3, 3.3-debug, 3.3-nocover, @@ -26,14 +18,10 @@ envlist = 3.4-debug, 3.4-nocover, 3.4-nocover-debug, - pypy-nix, - pypy-nix-debug, - pypy-nix-nocover, - pypy-nix-nocover-debug, - pypy-win, - pypy-win-debug, - pypy-win-nocover, - pypy-win-nocover-debug, + pypy, + pypy-debug, + pypy-nocover, + pypy-nocover-debug, report, docs @@ -79,7 +67,7 @@ deps = matrix usedevelop = true commands = - python configure + python bootstrap.py [testenv:check] basepython = python3.4 @@ -115,7 +103,7 @@ commands = coverage erase usedevelop = true deps = coverage -[testenv:2.6-nix] +[testenv:2.6] basepython = python2.6 setenv = {[testenv]setenv} @@ -128,9 +116,8 @@ deps = coverage pytest-cov trollius - MySQL-python -[testenv:2.6-nix-debug] +[testenv:2.6-debug] basepython = python2.6 setenv = {[testenv]setenv} @@ -144,18 +131,16 @@ deps = coverage pytest-cov trollius - MySQL-python -[testenv:2.6-nix-nocover] +[testenv:2.6-nocover] basepython = python2.6 deps = {[testenv]deps} coverage pytest-cov trollius - MySQL-python -[testenv:2.6-nix-nocover-debug] +[testenv:2.6-nocover-debug] basepython = python2.6 setenv = {[testenv]setenv} @@ -165,109 +150,8 @@ deps = coverage pytest-cov trollius - MySQL-python -[testenv:2.6-win] -basepython = python2.6 -setenv = - {[testenv]setenv} - WITH_COVERAGE=yes -usedevelop = true -commands = - {posargs:py.test --cov src -vv} -deps = - {[testenv]deps} - coverage - pytest-cov - trollius - -[testenv:2.6-win-debug] -basepython = python2.6 -setenv = - {[testenv]setenv} - ASPECTLIB_DEBUG=yes - WITH_COVERAGE=yes -usedevelop = true -commands = - {posargs:py.test --cov src -vv} -deps = - {[testenv]deps} - coverage - pytest-cov - trollius - -[testenv:2.6-win-nocover] -basepython = python2.6 -deps = - {[testenv]deps} - coverage - pytest-cov - trollius - -[testenv:2.6-win-nocover-debug] -basepython = python2.6 -setenv = - {[testenv]setenv} - ASPECTLIB_DEBUG=yes -deps = - {[testenv]deps} - coverage - pytest-cov - trollius - -[testenv:2.7-nix] -basepython = python2.7 -setenv = - {[testenv]setenv} - WITH_COVERAGE=yes -usedevelop = true -commands = - {posargs:py.test --cov src -vv} -deps = - {[testenv]deps} - coverage - pytest-cov - trollius - MySQL-python - -[testenv:2.7-nix-debug] -basepython = python2.7 -setenv = - {[testenv]setenv} - ASPECTLIB_DEBUG=yes - WITH_COVERAGE=yes -usedevelop = true -commands = - {posargs:py.test --cov src -vv} -deps = - {[testenv]deps} - coverage - pytest-cov - trollius - MySQL-python - -[testenv:2.7-nix-nocover] -basepython = python2.7 -deps = - {[testenv]deps} - coverage - pytest-cov - trollius - MySQL-python - -[testenv:2.7-nix-nocover-debug] -basepython = python2.7 -setenv = - {[testenv]setenv} - ASPECTLIB_DEBUG=yes -deps = - {[testenv]deps} - coverage - pytest-cov - trollius - MySQL-python - -[testenv:2.7-win] +[testenv:2.7] basepython = python2.7 setenv = {[testenv]setenv} @@ -281,7 +165,7 @@ deps = pytest-cov trollius -[testenv:2.7-win-debug] +[testenv:2.7-debug] basepython = python2.7 setenv = {[testenv]setenv} @@ -296,7 +180,7 @@ deps = pytest-cov trollius -[testenv:2.7-win-nocover] +[testenv:2.7-nocover] basepython = python2.7 deps = {[testenv]deps} @@ -304,7 +188,7 @@ deps = pytest-cov trollius -[testenv:2.7-win-nocover-debug] +[testenv:2.7-nocover-debug] basepython = python2.7 setenv = {[testenv]setenv} @@ -407,59 +291,7 @@ deps = coverage pytest-cov -[testenv:pypy-nix] -basepython = pypy -setenv = - {[testenv]setenv} - WITH_COVERAGE=yes -usedevelop = true -commands = - {posargs:py.test --cov src -vv} -deps = - {[testenv]deps} - coverage - pytest-cov - trollius - MySQL-python - -[testenv:pypy-nix-debug] -basepython = pypy -setenv = - {[testenv]setenv} - ASPECTLIB_DEBUG=yes - WITH_COVERAGE=yes -usedevelop = true -commands = - {posargs:py.test --cov src -vv} -deps = - {[testenv]deps} - coverage - pytest-cov - trollius - MySQL-python - -[testenv:pypy-nix-nocover] -basepython = pypy -deps = - {[testenv]deps} - coverage - pytest-cov - trollius - MySQL-python - -[testenv:pypy-nix-nocover-debug] -basepython = pypy -setenv = - {[testenv]setenv} - ASPECTLIB_DEBUG=yes -deps = - {[testenv]deps} - coverage - pytest-cov - trollius - MySQL-python - -[testenv:pypy-win] +[testenv:pypy] basepython = pypy setenv = {[testenv]setenv} @@ -473,7 +305,7 @@ deps = pytest-cov trollius -[testenv:pypy-win-debug] +[testenv:pypy-debug] basepython = pypy setenv = {[testenv]setenv} @@ -488,7 +320,7 @@ deps = pytest-cov trollius -[testenv:pypy-win-nocover] +[testenv:pypy-nocover] basepython = pypy deps = {[testenv]deps} @@ -496,7 +328,7 @@ deps = pytest-cov trollius -[testenv:pypy-win-nocover-debug] +[testenv:pypy-nocover-debug] basepython = pypy setenv = {[testenv]setenv} From 22c24ed24ab5ee99537b534312c662aac8d6ea4b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ionel=20Cristian=20M=C4=83rie=C8=99?= Date: Tue, 24 Jun 2014 04:48:30 +0300 Subject: [PATCH 045/179] Update manifest. --- MANIFEST.in | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/MANIFEST.in b/MANIFEST.in index 96470a0..6112c1c 100644 --- a/MANIFEST.in +++ b/MANIFEST.in @@ -1,6 +1,6 @@ graft docs graft examples -graft templates +graft conf graft src graft tests @@ -16,7 +16,7 @@ include CONTRIBUTING.rst include LICENSE include README.rst -include configure +include bootstrap.py include tox.ini .travis.yml appveyor.yml include conftest.py From 0d6cde97b9f6b177d27c351f7a3a46111628abd6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ionel=20Cristian=20M=C4=83rie=C8=99?= Date: Tue, 24 Jun 2014 04:50:16 +0300 Subject: [PATCH 046/179] Update check env. Fix test settings. --- conf/tox.ini | 2 ++ setup.cfg | 6 +++++- tox.ini | 2 ++ 3 files changed, 9 insertions(+), 1 deletion(-) diff --git a/conf/tox.ini b/conf/tox.ini index 67475dd..572cf73 100644 --- a/conf/tox.ini +++ b/conf/tox.ini @@ -58,8 +58,10 @@ deps = docutils check-manifest flake8 + collective.checkdocs usedevelop = true commands = + python setup.py checkdocs python setup.py check --restructuredtext --strict --metadata check-manifest {toxinidir} flake8 src diff --git a/setup.cfg b/setup.cfg index 6a3304a..6f78ca0 100644 --- a/setup.cfg +++ b/setup.cfg @@ -11,13 +11,17 @@ norecursedirs = .tox dist build +python_files = + test_*.py + *_test.py + tests.py addopts = -rxEfs --strict --ignore docs/conf.py --ignore setup.py + --ignore bootstrap.py --doctest-modules - --doctest-glob \*.rst --tb short --cov-report term-missing diff --git a/tox.ini b/tox.ini index 6f78ea1..c54cd6d 100644 --- a/tox.ini +++ b/tox.ini @@ -75,8 +75,10 @@ deps = docutils check-manifest flake8 + collective.checkdocs usedevelop = true commands = + python setup.py checkdocs python setup.py check --restructuredtext --strict --metadata check-manifest {toxinidir} flake8 src From 10401b5bf6e62aea402d232cb00ae8751e5274ef Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ionel=20Cristian=20M=C4=83rie=C8=99?= Date: Tue, 24 Jun 2014 22:36:10 +0300 Subject: [PATCH 047/179] Fix patching properties defined on baseclases when there's more than 1 baseclass. --- src/aspectlib/__init__.py | 15 ++++++++-- tests/test_aspectlib.py | 62 +++++++++++++++++++++++++++++++++++++++ 2 files changed, 75 insertions(+), 2 deletions(-) diff --git a/src/aspectlib/__init__.py b/src/aspectlib/__init__.py index fcf11f3..4f117b4 100644 --- a/src/aspectlib/__init__.py +++ b/src/aspectlib/__init__.py @@ -592,7 +592,7 @@ def weave_class(klass, aspect, methods=NORMAL_METHODS, subclasses=True, lazy=Fal for sub_class in sub_targets: if not issubclass(sub_class, Fabric): entanglement.merge(weave_class(sub_class, aspect, - methods=methods, subclasses=subclasses, lazy=lazy, bases=False, bag=bag)) + methods=methods, subclasses=subclasses, lazy=lazy, bag=bag)) if lazy: def __init__(self, *args, **kwargs): super(SubClass, self).__init__(*args, **kwargs) @@ -630,7 +630,7 @@ def __init__(self, *args, **kwargs): ), maxlen=0)) if bases: super_original = set() - for sklass in klass.__bases__: + for sklass in _find_super_classes(klass): if sklass is not object: for attr, func in sklass.__dict__.items(): if method_matches(attr) and attr not in original and attr not in super_original: @@ -648,6 +648,17 @@ def __init__(self, *args, **kwargs): return entanglement +def _find_super_classes(klass): + if hasattr(klass, '__mro__'): + for k in klass.__mro__: + yield k + else: + for base in klass.__bases__: + yield base + for k in _find_super_classes(base): + yield k + + def patch_module(module, name, replacement, original=UNSPECIFIED, aliases=True, location=None, **_bogus_options): """ Low-level attribute patcher. diff --git a/tests/test_aspectlib.py b/tests/test_aspectlib.py index 2d8ee80..2ab277d 100644 --- a/tests/test_aspectlib.py +++ b/tests/test_aspectlib.py @@ -117,6 +117,9 @@ def class_foobar(cls, foo, bar=None): def static_foobar(foo, bar=None): return bar or foo + def only_in_base(self): + return self.__class__.__name__ + class LegacyTestSubClass(LegacyTestClass): def foobar(self, foo, bar=None): @@ -570,6 +573,65 @@ def aspect(*args): assert inst.only_in_base() == 'NormalTestSubClass' +def test_weave_subclass_meth_from_baseclass_2_level(): + history = [] + + @aspectlib.Aspect + def aspect(*args): + result = yield + history.append(args + (result,)) + yield aspectlib.Return('bar-'+result) + + with aspectlib.weave(NormalTestSubSubClass.only_in_base, aspect): + inst = NormalTestSubSubClass('stuff') + assert inst.only_in_base() == 'bar-NormalTestSubSubClass' + assert history == [ + (inst, 'NormalTestSubSubClass'), + ] + + inst = NormalTestSubSubClass('stuff') + assert inst.only_in_base() == 'NormalTestSubSubClass' + + +def test_weave_legacy_subclass_meth_from_baseclass(): + history = [] + + @aspectlib.Aspect + def aspect(*args): + result = yield + history.append(args + (result,)) + yield aspectlib.Return('bar-'+result) + + with aspectlib.weave(LegacyTestSubClass.only_in_base, aspect): + inst = LegacyTestSubClass('stuff') + assert inst.only_in_base() == 'bar-LegacyTestSubClass' + assert history == [ + (inst, 'LegacyTestSubClass'), + ] + + inst = LegacyTestSubClass('stuff') + assert inst.only_in_base() == 'LegacyTestSubClass' + + +def test_weave_legacy_subclass_meth_from_baseclass_2_level(): + history = [] + + @aspectlib.Aspect + def aspect(*args): + result = yield + history.append(args + (result,)) + yield aspectlib.Return('bar-'+result) + + with aspectlib.weave(LegacyTestSubSubClass.only_in_base, aspect): + inst = LegacyTestSubSubClass('stuff') + assert inst.only_in_base() == 'bar-LegacyTestSubSubClass' + assert history == [ + (inst, 'LegacyTestSubSubClass'), + ] + + inst = LegacyTestSubSubClass('stuff') + assert inst.only_in_base() == 'LegacyTestSubSubClass' + def test_weave_class(): history = [] From b3020a1f004e30e92b8f39a53e76f5dd8404749e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ionel=20Cristian=20M=C4=83rie=C8=99?= Date: Tue, 24 Jun 2014 22:40:08 +0300 Subject: [PATCH 048/179] Update changelog. --- docs/changelog.rst | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/docs/changelog.rst b/docs/changelog.rst index 4df717e..ea7af51 100644 --- a/docs/changelog.rst +++ b/docs/changelog.rst @@ -1,6 +1,13 @@ Changelog ========= +Version 1.2.0 +------------- + +* Fix weaving methods that were defined in some baseclass (and not on the target class). +* Fix wrong qualname beeing used in the Story/Replay recording. Now used the alias given to the weaver instead of + whatever is the realname on the current platform. + Version 1.1.1 ------------- From 3c7327f1a1f8501952c86542d17d3ad467fe364a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ionel=20Cristian=20M=C4=83rie=C8=99?= Date: Tue, 24 Jun 2014 22:40:15 +0300 Subject: [PATCH 049/179] =?UTF-8?q?Bump=20version:=201.1.1=20=E2=86=92=201?= =?UTF-8?q?.2.0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .bumpversion.cfg | 2 +- setup.py | 2 +- src/aspectlib/__init__.py | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/.bumpversion.cfg b/.bumpversion.cfg index 25640ce..57f0afb 100644 --- a/.bumpversion.cfg +++ b/.bumpversion.cfg @@ -1,5 +1,5 @@ [bumpversion] -current_version = 1.1.1 +current_version = 1.2.0 files = setup.py src/aspectlib/__init__.py commit = True tag = False diff --git a/setup.py b/setup.py index 3a59fe2..c48ff68 100644 --- a/setup.py +++ b/setup.py @@ -19,7 +19,7 @@ def read(*names, **kwargs): setup( name="aspectlib", - version="1.1.1", + version="1.2.0", url='https://github.com/ionelmc/python-aspectlib', download_url='', license='BSD', diff --git a/src/aspectlib/__init__.py b/src/aspectlib/__init__.py index 4f117b4..0f755e9 100644 --- a/src/aspectlib/__init__.py +++ b/src/aspectlib/__init__.py @@ -36,7 +36,7 @@ __all__ = 'weave', 'Aspect', 'Proceed', 'Return', 'ALL_METHODS', 'NORMAL_METHODS' -__version__ = '1.1.1' +__version__ = '1.2.0' logger = getLogger(__name__) logdebug = logf(logger.debug) From b4b0bd5a24c18be0d4a00654f084e9b0dfcc695a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ionel=20Cristian=20M=C4=83rie=C8=99?= Date: Wed, 25 Jun 2014 10:11:58 +0300 Subject: [PATCH 050/179] Update gitignore. --- .gitignore | 11 +++++------ 1 file changed, 5 insertions(+), 6 deletions(-) diff --git a/.gitignore b/.gitignore index 615a08c..23606f4 100644 --- a/.gitignore +++ b/.gitignore @@ -24,6 +24,8 @@ pip-log.txt # Unit test / coverage reports .coverage .tox +.coverage +.coverage.* nosetests.xml htmlcov @@ -45,9 +47,6 @@ docs/_build .DS_Store *~ .*.sw[po] -.build/ -.tox/ -.coverage -.coverage.* -.ve/ -.bootstrap/ \ No newline at end of file +.build +.ve +.bootstrap \ No newline at end of file From fe1fb6c6f17844d96cb1661e4203dd31003bba7d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ionel=20Cristian=20M=C4=83rie=C8=99?= Date: Tue, 12 Aug 2014 22:24:10 +0300 Subject: [PATCH 051/179] Make some objects "private" --- src/aspectlib/test.py | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/src/aspectlib/test.py b/src/aspectlib/test.py index a65bacb..50a86b8 100644 --- a/src/aspectlib/test.py +++ b/src/aspectlib/test.py @@ -65,7 +65,7 @@ def mock_wrapper(*args, **kwargs): return mock_decorator -class RecordingFunctionWrapper(object): +class _RecordingFunctionWrapper(object): """ Function wrapper that records calls and can be used as an weaver context manager. @@ -123,7 +123,7 @@ def __record(self, args, kwargs, *response): )) def __get__(self, instance, owner): - return RecordingFunctionWrapper( + return _RecordingFunctionWrapper( self.__wrapped.__get__(instance, owner), iscalled=self.__iscalled, calls=self.calls, @@ -193,7 +193,7 @@ def record(func=None, recurse_lock_factory=allocate_lock, **options): Added `extended` option. """ if func: - return RecordingFunctionWrapper( + return _RecordingFunctionWrapper( func, recurse_lock=recurse_lock_factory(), **options @@ -233,7 +233,7 @@ def __unsupported__(self, *args): exec("%s = __unsupported__" % mm) -class StoryFunctionWrapper(object): +class _StoryFunctionWrapper(object): def __init__(self, wrapped, handle, binding=None, owner=None): self._wrapped = wrapped self._name = wrapped.__name__ @@ -263,7 +263,7 @@ def __get__(self, binding, owner): ), self) -class ReplayFunctionWrapper(StoryFunctionWrapper): +class _ReplayFunctionWrapper(_StoryFunctionWrapper): def __call__(self, *args, **kwargs): if self._binding is None: return self._handle(None, self._qualname, args, kwargs, self._wrapped) @@ -321,7 +321,7 @@ def __enter__(self): self._options.setdefault('methods', ALL_METHODS) self.__entanglement = weave( self._target, - partial(self.FunctionWrapper, handle=self._handle), + partial(self._FunctionWrapper, handle=self._handle), **self._options ) return self @@ -380,7 +380,7 @@ class Story(_RecordingBase): .. [1] http://www.martinfowler.com/bliki/TestDouble.html """ - FunctionWrapper = StoryFunctionWrapper + _FunctionWrapper = _StoryFunctionWrapper def __init__(self, *args, **kwargs): super(Story, self).__init__(*args, **kwargs) @@ -446,7 +446,7 @@ class Replay(_RecordingBase): This object should be created by :obj:`Story `'s :obj:`replay ` method. """ - FunctionWrapper = ReplayFunctionWrapper + _FunctionWrapper = _ReplayFunctionWrapper def __init__(self, play, proxy=True, strict=True, dump=True, recurse_lock=False, **options): super(Replay, self).__init__(play._target, **options) From 0c5febb14a3619e242405ca380e92d53eebb71e8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ionel=20Cristian=20M=C4=83rie=C8=99?= Date: Tue, 12 Aug 2014 23:54:59 +0300 Subject: [PATCH 052/179] Improve log messages in patch_module. --- src/aspectlib/__init__.py | 13 ++++++++----- 1 file changed, 8 insertions(+), 5 deletions(-) diff --git a/src/aspectlib/__init__.py b/src/aspectlib/__init__.py index 0f755e9..3196483 100644 --- a/src/aspectlib/__init__.py +++ b/src/aspectlib/__init__.py @@ -675,33 +675,36 @@ def patch_module(module, name, replacement, original=UNSPECIFIED, aliases=True, seen = False original = getattr(module, name) if original is UNSPECIFIED else original location = module.__name__ if hasattr(module, '__name__') else type(module).__module__ + target = module.__name__ if hasattr(module, '__name__') else type(module).__name__ try: replacement.__module__ = location except (TypeError, AttributeError): pass for alias in dir(module): + logdebug("alias:%s (%s)", alias, name) if hasattr(module, alias): obj = getattr(module, alias) + logdebug("- %s:%s (%s)", obj, original, obj is original) if obj is original: if aliases or alias == name: - logdebug("= saving %s on %s.%s ...", replacement, location, alias) + logdebug("= saving %s on %s.%s ...", replacement, target, alias) setattr(module, alias, replacement) rollback.merge(lambda alias=alias: setattr(module, alias, original)) if alias == name: seen = True elif alias == name: if ismethod(obj): - logdebug("= saving %s on %s.%s ...", replacement, location, alias) + logdebug("= saving %s on %s.%s ...", replacement, target, alias) setattr(module, alias, replacement) rollback.merge(lambda alias=alias: setattr(module, alias, original)) else: - raise AssertionError("%s.%s = %s is not %s." % (location, alias, obj, original)) + raise AssertionError("%s.%s = %s is not %s." % (module, alias, obj, original)) if not seen: warnings.warn('Setting %s.%s to %s. There was no previous definition, probably patching the wrong module.' % ( - location, name, replacement + target, name, replacement )) - logdebug("= saving %s on %s.%s ...", replacement, location, name) + logdebug("= saving %s on %s.%s ...", replacement, target, name) setattr(module, name, replacement) rollback.merge(lambda: setattr(module, name, original)) return rollback From a0dc66eead6e9bca2d98f72d8f96e78c2dd7fd4b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ionel=20Cristian=20M=C4=83rie=C8=99?= Date: Tue, 12 Aug 2014 23:55:15 +0300 Subject: [PATCH 053/179] Add a LogCapture context manager. --- src/aspectlib/test.py | 61 ++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 60 insertions(+), 1 deletion(-) diff --git a/src/aspectlib/test.py b/src/aspectlib/test.py index 50a86b8..9c63b07 100644 --- a/src/aspectlib/test.py +++ b/src/aspectlib/test.py @@ -1,11 +1,12 @@ -from sys import _getframe from collections import defaultdict from collections import namedtuple from difflib import unified_diff from functools import partial from functools import wraps from inspect import isclass +from logging import _checkLevel from logging import getLogger +from sys import _getframe from traceback import format_stack from aspectlib import ALL_METHODS @@ -65,6 +66,64 @@ def mock_wrapper(*args, **kwargs): return mock_decorator +class LogCapture(object): + """ + Records all log messages made on the given logger. Assumes the logger has a ``_log`` method. + + Example:: + + >>> import logging + >>> logger = logging.getLogger('mylogger') + >>> with LogCapture(logger, level='INFO') as logs: + ... logger.debug("Message from debug: %s", 'somearg') + ... logger.info("Message from info: %s", 'somearg') + ... logger.error("Message from error: %s", 'somearg') + >>> logs.calls + [('Message from info: %s', ('somearg',), 20), ('Message from error: %s', ('somearg',), 40)] + >>> logs.has('Message from info: %s') + True + >>> logs.has('Message from debug: %s') + False + >>> logs.assertLogged('Message from error: %s', ) + + """ + def __init__(self, logger, level='DEBUG'): + self.logger = logger + self.level = _checkLevel(level) + self.calls = [] + self.rollback = None + + def __enter__(self): + self.rollback = weave( + self.logger, + record(callback=self._callback, extended=True, iscalled=True), + methods='_log$' + ) + return self + + def __exit__(self, *exc): + self.rollback() + + def _callback(self, _binding, _qualname, args, _kwargs): + level, message, args = args + if level >= self.level: + self.calls.append((message, args, level)) + + def has(self, message, args=None, level=None): + #level = kwargs.pop('level', None) + #assert not kwargs, "Unexpected arguments: %s" % kwargs + for call_message, call_args, call_level in self.calls: + if message == call_message and (args is None or args == call_args) and (level is None or level == call_level): + return True + return False + + def assertLogged(self, message, level=None, args=None): + if not self.has(message, level, args): + raise AssertionError("There's no such message %r (with args %r) logged on %s. Logged messages where: %s" % ( + message, args, self.logger, self.calls + )) + + class _RecordingFunctionWrapper(object): """ Function wrapper that records calls and can be used as an weaver context manager. From c58ff931c7d3f78f2554f9d76683fbd136c4cb55 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ionel=20Cristian=20M=C4=83rie=C8=99?= Date: Wed, 13 Aug 2014 02:29:40 +0300 Subject: [PATCH 054/179] Make assertLogged/has more flexible. --- src/aspectlib/test.py | 55 ++++++++++++++++++++++++++++--------------- 1 file changed, 36 insertions(+), 19 deletions(-) diff --git a/src/aspectlib/test.py b/src/aspectlib/test.py index 9c63b07..0de32fe 100644 --- a/src/aspectlib/test.py +++ b/src/aspectlib/test.py @@ -82,45 +82,62 @@ class LogCapture(object): [('Message from info: %s', ('somearg',), 20), ('Message from error: %s', ('somearg',), 40)] >>> logs.has('Message from info: %s') True + >>> logs.has('Message from info: somearg') + True + >>> logs.has('Message from info: %s', 'badarg') + False >>> logs.has('Message from debug: %s') False - >>> logs.assertLogged('Message from error: %s', ) + >>> logs.assertLogged('Message from error: %s') + >>> logs.assertLogged('Message from error: %s') + >>> logs.assertLogged('Message from error: %s') + """ def __init__(self, logger, level='DEBUG'): - self.logger = logger - self.level = _checkLevel(level) - self.calls = [] - self.rollback = None + self._logger = logger + self._level = _checkLevel(level) + self._calls = [] + self._rollback = None def __enter__(self): - self.rollback = weave( - self.logger, + self._rollback = weave( + self._logger, record(callback=self._callback, extended=True, iscalled=True), methods='_log$' ) return self def __exit__(self, *exc): - self.rollback() + self._rollback() def _callback(self, _binding, _qualname, args, _kwargs): level, message, args = args - if level >= self.level: - self.calls.append((message, args, level)) - - def has(self, message, args=None, level=None): - #level = kwargs.pop('level', None) - #assert not kwargs, "Unexpected arguments: %s" % kwargs - for call_message, call_args, call_level in self.calls: - if message == call_message and (args is None or args == call_args) and (level is None or level == call_level): + if level >= self._level: + self._calls.append((message % args if args else message, message, args, level)) + + @property + def calls(self): + return [i[1:] for i in self._calls] + + def has(self, message, *args, **kwargs): + level = kwargs.pop('level', None) + assert not kwargs, "Unexpected arguments: %s" % kwargs + for call_final_message, call_message, call_args, call_level in self._calls: + if ( + (level is None or level == call_level) and ( + message == call_message and args == call_args + if args else + message == call_final_message or message == call_message + ) + ): return True return False - def assertLogged(self, message, level=None, args=None): - if not self.has(message, level, args): + def assertLogged(self, message, *args, **kwargs): + if not self.has(message, *args, **kwargs): raise AssertionError("There's no such message %r (with args %r) logged on %s. Logged messages where: %s" % ( - message, args, self.logger, self.calls + message, args, self._logger, self.calls )) From 00e420b33342ec35f6a7eb5760e0ac0c88dc03aa Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ionel=20Cristian=20M=C4=83rie=C8=99?= Date: Wed, 13 Aug 2014 02:37:20 +0300 Subject: [PATCH 055/179] Fix getting log level number. --- src/aspectlib/test.py | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/src/aspectlib/test.py b/src/aspectlib/test.py index 0de32fe..4a880d0 100644 --- a/src/aspectlib/test.py +++ b/src/aspectlib/test.py @@ -4,7 +4,10 @@ from functools import partial from functools import wraps from inspect import isclass -from logging import _checkLevel +try: + from logging import _levelNames as nameToLevel +except ImportError: + from logging import _nameToLevel as nameToLevel from logging import getLogger from sys import _getframe from traceback import format_stack @@ -96,7 +99,7 @@ class LogCapture(object): """ def __init__(self, logger, level='DEBUG'): self._logger = logger - self._level = _checkLevel(level) + self._level = nameToLevel[level] self._calls = [] self._rollback = None From a6f9111a952b6cc495f73328a1a31a9056ec11b8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ionel=20Cristian=20M=C4=83rie=C8=99?= Date: Wed, 13 Aug 2014 02:44:56 +0300 Subject: [PATCH 056/179] Add support for InstanceTypes. --- src/aspectlib/__init__.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/aspectlib/__init__.py b/src/aspectlib/__init__.py index 3196483..51ec760 100644 --- a/src/aspectlib/__init__.py +++ b/src/aspectlib/__init__.py @@ -12,6 +12,7 @@ from inspect import ismethoddescriptor from inspect import ismodule from inspect import isroutine +from types import InstanceType from functools import partial from logging import getLogger @@ -484,7 +485,7 @@ def weave(target, aspects, **options): return weave_class(target, aspects, **options) elif ismodule(target): return weave_module(target, aspects, **options) - elif type(target).__module__ not in ('builtins', '__builtin__'): + elif type(target).__module__ not in ('builtins', '__builtin__') or isinstance(target, InstanceType): return weave_instance(target, aspects, **options) else: raise UnsupportedType("Can't weave object %s of type %s" % (target, type(target))) From fc081284a31e645f752a6d522a7a70dbbd497f17 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ionel=20Cristian=20M=C4=83rie=C8=99?= Date: Wed, 13 Aug 2014 03:04:04 +0300 Subject: [PATCH 057/179] Fix handling for py3. --- src/aspectlib/__init__.py | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/src/aspectlib/__init__.py b/src/aspectlib/__init__.py index 51ec760..c822874 100644 --- a/src/aspectlib/__init__.py +++ b/src/aspectlib/__init__.py @@ -12,7 +12,10 @@ from inspect import ismethoddescriptor from inspect import ismodule from inspect import isroutine -from types import InstanceType +try: + from types import InstanceType +except ImportError: + InstanceType = None from functools import partial from logging import getLogger @@ -485,7 +488,7 @@ def weave(target, aspects, **options): return weave_class(target, aspects, **options) elif ismodule(target): return weave_module(target, aspects, **options) - elif type(target).__module__ not in ('builtins', '__builtin__') or isinstance(target, InstanceType): + elif type(target).__module__ not in ('builtins', '__builtin__') or InstanceType and isinstance(target, InstanceType): return weave_instance(target, aspects, **options) else: raise UnsupportedType("Can't weave object %s of type %s" % (target, type(target))) From ae5552c198c9662a5f596ea83cf5deb42222d633 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ionel=20Cristian=20M=C4=83rie=C8=99?= Date: Thu, 16 Oct 2014 02:02:33 +0300 Subject: [PATCH 058/179] Don't raise exceptions from Replay.__exit__ if there would be an error (makes original cause hard to debug). --- src/aspectlib/test.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/aspectlib/test.py b/src/aspectlib/test.py index 4a880d0..31f48a1 100644 --- a/src/aspectlib/test.py +++ b/src/aspectlib/test.py @@ -649,17 +649,17 @@ def actual(self): def expected(self): return ''.join(_format_calls(self._expected)) - def __exit__(self, *args): + def __exit__(self, *exception): super(Replay, self).__exit__() if self._strict or self._dump: diff = self.diff if diff: - if self._dump: + if exception or self._dump: print('STORY/REPLAY DIFF:') print(' ' + '\n '.join(diff.splitlines())) print('ACTUAL:') print(' ' + ' '.join(_format_calls(self._actual))) - if self._strict: + if not exception and self._strict: raise AssertionError(diff) From 3ff99799afac6638864b87ac81c0311e9302b8d1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ionel=20Cristian=20M=C4=83rie=C8=99?= Date: Thu, 16 Oct 2014 02:04:18 +0300 Subject: [PATCH 059/179] Update changelog. --- docs/changelog.rst | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/docs/changelog.rst b/docs/changelog.rst index ea7af51..e844054 100644 --- a/docs/changelog.rst +++ b/docs/changelog.rst @@ -1,6 +1,11 @@ Changelog ========= +Version 1.2.1 +------------- + +* Don't raise exceptions from Replay.__exit__ if there would be an error (makes original cause hard to debug). + Version 1.2.0 ------------- From f59f2d135c1e1f140aeb6c774da65f56466c4e0a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ionel=20Cristian=20M=C4=83rie=C8=99?= Date: Thu, 16 Oct 2014 02:04:45 +0300 Subject: [PATCH 060/179] =?UTF-8?q?Bump=20version:=201.2.0=20=E2=86=92=201?= =?UTF-8?q?.2.1?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .bumpversion.cfg | 2 +- setup.py | 2 +- src/aspectlib/__init__.py | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/.bumpversion.cfg b/.bumpversion.cfg index 57f0afb..0319bff 100644 --- a/.bumpversion.cfg +++ b/.bumpversion.cfg @@ -1,5 +1,5 @@ [bumpversion] -current_version = 1.2.0 +current_version = 1.2.1 files = setup.py src/aspectlib/__init__.py commit = True tag = False diff --git a/setup.py b/setup.py index c48ff68..aa77f3f 100644 --- a/setup.py +++ b/setup.py @@ -19,7 +19,7 @@ def read(*names, **kwargs): setup( name="aspectlib", - version="1.2.0", + version="1.2.1", url='https://github.com/ionelmc/python-aspectlib', download_url='', license='BSD', diff --git a/src/aspectlib/__init__.py b/src/aspectlib/__init__.py index c822874..cd4bd0a 100644 --- a/src/aspectlib/__init__.py +++ b/src/aspectlib/__init__.py @@ -40,7 +40,7 @@ __all__ = 'weave', 'Aspect', 'Proceed', 'Return', 'ALL_METHODS', 'NORMAL_METHODS' -__version__ = '1.2.0' +__version__ = '1.2.1' logger = getLogger(__name__) logdebug = logf(logger.debug) From 5eb44259539c4f5c2dc5ccd6d8354961ea0a8ea0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ionel=20Cristian=20M=C4=83rie=C8=99?= Date: Mon, 10 Nov 2014 17:46:05 +0200 Subject: [PATCH 061/179] Fix encoding for windows. --- setup.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/setup.py b/setup.py index aa77f3f..0f738da 100644 --- a/setup.py +++ b/setup.py @@ -1,4 +1,4 @@ -# -*- encoding: utf8 -*- +# -*- encoding: utf-8 -*- import glob import io import re From 450ba3cc6d687c1872523e6455ff02ffe24f7050 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ionel=20Cristian=20M=C4=83rie=C8=99?= Date: Tue, 25 Nov 2014 12:33:43 +0200 Subject: [PATCH 062/179] Fix coverage dep. --- conf/tox.ini | 1 - tox.ini | 20 -------------------- 2 files changed, 21 deletions(-) diff --git a/conf/tox.ini b/conf/tox.ini index 572cf73..2aa5d2c 100644 --- a/conf/tox.ini +++ b/conf/tox.ini @@ -106,7 +106,6 @@ commands = {% endif %} deps = {[testenv]deps} - coverage pytest-cov {% for dep in config.deps %} {{ dep }} diff --git a/tox.ini b/tox.ini index c54cd6d..fcef147 100644 --- a/tox.ini +++ b/tox.ini @@ -115,7 +115,6 @@ commands = {posargs:py.test --cov src -vv} deps = {[testenv]deps} - coverage pytest-cov trollius @@ -130,7 +129,6 @@ commands = {posargs:py.test --cov src -vv} deps = {[testenv]deps} - coverage pytest-cov trollius @@ -138,7 +136,6 @@ deps = basepython = python2.6 deps = {[testenv]deps} - coverage pytest-cov trollius @@ -149,7 +146,6 @@ setenv = ASPECTLIB_DEBUG=yes deps = {[testenv]deps} - coverage pytest-cov trollius @@ -163,7 +159,6 @@ commands = {posargs:py.test --cov src -vv} deps = {[testenv]deps} - coverage pytest-cov trollius @@ -178,7 +173,6 @@ commands = {posargs:py.test --cov src -vv} deps = {[testenv]deps} - coverage pytest-cov trollius @@ -186,7 +180,6 @@ deps = basepython = python2.7 deps = {[testenv]deps} - coverage pytest-cov trollius @@ -197,7 +190,6 @@ setenv = ASPECTLIB_DEBUG=yes deps = {[testenv]deps} - coverage pytest-cov trollius @@ -211,7 +203,6 @@ commands = {posargs:py.test --cov src -vv} deps = {[testenv]deps} - coverage pytest-cov asyncio @@ -226,7 +217,6 @@ commands = {posargs:py.test --cov src -vv} deps = {[testenv]deps} - coverage pytest-cov asyncio @@ -234,7 +224,6 @@ deps = basepython = python3.3 deps = {[testenv]deps} - coverage pytest-cov asyncio @@ -245,7 +234,6 @@ setenv = ASPECTLIB_DEBUG=yes deps = {[testenv]deps} - coverage pytest-cov asyncio @@ -259,7 +247,6 @@ commands = {posargs:py.test --cov src -vv} deps = {[testenv]deps} - coverage pytest-cov [testenv:3.4-debug] @@ -273,14 +260,12 @@ commands = {posargs:py.test --cov src -vv} deps = {[testenv]deps} - coverage pytest-cov [testenv:3.4-nocover] basepython = python3.4 deps = {[testenv]deps} - coverage pytest-cov [testenv:3.4-nocover-debug] @@ -290,7 +275,6 @@ setenv = ASPECTLIB_DEBUG=yes deps = {[testenv]deps} - coverage pytest-cov [testenv:pypy] @@ -303,7 +287,6 @@ commands = {posargs:py.test --cov src -vv} deps = {[testenv]deps} - coverage pytest-cov trollius @@ -318,7 +301,6 @@ commands = {posargs:py.test --cov src -vv} deps = {[testenv]deps} - coverage pytest-cov trollius @@ -326,7 +308,6 @@ deps = basepython = pypy deps = {[testenv]deps} - coverage pytest-cov trollius @@ -337,7 +318,6 @@ setenv = ASPECTLIB_DEBUG=yes deps = {[testenv]deps} - coverage pytest-cov trollius From 5a32568f58c216de9238c02d5d2f9278dcada225 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ionel=20Cristian=20M=C4=83rie=C8=99?= Date: Tue, 25 Nov 2014 12:34:23 +0200 Subject: [PATCH 063/179] Add support for weakref in aspectlib.debug.log. Fixes #1. --- src/aspectlib/debug.py | 2 +- tests/test_aspectlib_debug.py | 42 ++++++++++++++++++++++++----------- 2 files changed, 30 insertions(+), 14 deletions(-) diff --git a/src/aspectlib/debug.py b/src/aspectlib/debug.py index efe7013..48a33bd 100644 --- a/src/aspectlib/debug.py +++ b/src/aspectlib/debug.py @@ -158,7 +158,7 @@ def dump(buf): logger.critical('Failed to log a message: %s', exc, exc_info=True) class __logged__(Aspect): - __slots__ = 'cutpoint_function', 'final_function', 'binding', '__name__' + __slots__ = 'cutpoint_function', 'final_function', 'binding', '__name__', '__weakref__' bind = False diff --git a/tests/test_aspectlib_debug.py b/tests/test_aspectlib_debug.py index 07c682a..b4d6ef3 100644 --- a/tests/test_aspectlib_debug.py +++ b/tests/test_aspectlib_debug.py @@ -2,28 +2,28 @@ import logging import re - -import aspectlib -import aspectlib.debug - -try: - import unittest2 as unittest -except ImportError: - import unittest - +import sys +import weakref try: from StringIO import StringIO except ImportError: from io import StringIO -def some_meth(*args, **kwargs): - return ''.join(chr(i) for i in range(255)) +import pytest + +import aspectlib +import aspectlib.debug + -LOG_TEST_SIMPLE = '''^some_meth\(1, 2, 3, a=4\) +<<< .*tests/test_aspectlib_debug.py:\d+:test_simple.* +LOG_TEST_SIMPLE = r'''^some_meth\(1, 2, 3, a=4\) +<<< .*tests/test_aspectlib_debug.py:\d+:test_simple.* some_meth => \.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\. !"#\$%&\'\(\)\*\+,-\./0123456789:;<=>\?@ABCDEFGHIJKLMNOPQRSTUVWXYZ\[\\\]\^_`abcdefghijklmnopqrstuvwxyz\{\|\}~\.+ $''' +def some_meth(*_args, **_kwargs): + return ''.join(chr(i) for i in range(255)) + + class MyStuff(object): def __init__(self, foo): self.foo = foo @@ -126,4 +126,20 @@ def test_no_stack_old_style(): print(buf.getvalue()) assert "{test_aspectlib_debug.OldStuff foo='bar' bar='foo'}.stuff()\n{test_aspectlib_debug.OldStuff foo='bar' bar='foo'}.stuff => bar\n" == buf.getvalue() -#test log with old-style class + +@pytest.mark.skipif(sys.version_info < (2, 7), reason="No weakref.WeakSet on Python<=2.6") +def test_weakref(): + with aspectlib.weave(MyStuff, aspectlib.debug.log): + s = weakref.WeakSet() + s.add(MyStuff.stuff) + print(list(s)) + print(list(s)) + + +@pytest.mark.skipif(sys.version_info < (2, 7), reason="No weakref.WeakSet on Python<=2.6") +def test_weakref_oldstyle(): + with aspectlib.weave(OldStuff, aspectlib.debug.log): + s = weakref.WeakSet() + s.add(MyStuff.stuff) + print(list(s)) + print(list(s)) From de6a1ffea93ae0a7fa5bc5ca0f6cb70688a173ed Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ionel=20Cristian=20M=C4=83rie=C8=99?= Date: Tue, 25 Nov 2014 12:34:39 +0200 Subject: [PATCH 064/179] Make some strings "raw". --- tests/test_aspectlib_debug.py | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/tests/test_aspectlib_debug.py b/tests/test_aspectlib_debug.py index b4d6ef3..1114c16 100644 --- a/tests/test_aspectlib_debug.py +++ b/tests/test_aspectlib_debug.py @@ -34,6 +34,7 @@ def bar(self): def stuff(self): return self.foo + class OldStuff: def __init__(self, foo): self.foo = foo @@ -72,7 +73,7 @@ def test_logging_works(): def foo(): pass foo() - assert re.match('foo\(\) +<<<.*\nfoo => None\n', buf.getvalue()) + assert re.match(r'foo\(\) +<<<.*\nfoo => None\n', buf.getvalue()) def test_attributes(): @@ -84,9 +85,9 @@ def test_attributes(): ), methods='(?!bar)(?!__.*__$)'): MyStuff('bar').stuff() print(buf.getvalue()) - assert re.match("^\{test_aspectlib_debug.MyStuff foo='bar' bar='foo'\}.stuff\(\) +<<< .*tests/test_aspectlib_debug.py:\d+:test_attributes.*\n\{test_aspectlib_debug.MyStuff foo='bar' bar='foo'\}.stuff => bar\n$", buf.getvalue()) + assert re.match(r"^\{test_aspectlib_debug.MyStuff foo='bar' bar='foo'\}.stuff\(\) +<<< .*tests/test_aspectlib_debug.py:\d+:test_attributes.*\n\{test_aspectlib_debug.MyStuff foo='bar' bar='foo'\}.stuff => bar\n$", buf.getvalue()) MyStuff('bar').stuff() - assert re.match("^\{test_aspectlib_debug.MyStuff foo='bar' bar='foo'\}.stuff\(\) +<<< .*tests/test_aspectlib_debug.py:\d+:test_attributes.*\n\{test_aspectlib_debug.MyStuff foo='bar' bar='foo'\}.stuff => bar\n$", buf.getvalue()) + assert re.match(r"^\{test_aspectlib_debug.MyStuff foo='bar' bar='foo'\}.stuff\(\) +<<< .*tests/test_aspectlib_debug.py:\d+:test_attributes.*\n\{test_aspectlib_debug.MyStuff foo='bar' bar='foo'\}.stuff => bar\n$", buf.getvalue()) def test_no_stack(): @@ -110,9 +111,9 @@ def test_attributes_old_style(): ), methods='(?!bar)(?!__.*__$)'): OldStuff('bar').stuff() print(repr(buf.getvalue())) - assert re.match("^\{test_aspectlib_debug.OldStuff foo='bar' bar='foo'\}.stuff\(\) +<<< .*tests/test_aspectlib_debug.py:\d+:test_attributes.*\n\{test_aspectlib_debug.OldStuff foo='bar' bar='foo'\}.stuff => bar\n$", buf.getvalue()) + assert re.match(r"^\{test_aspectlib_debug.OldStuff foo='bar' bar='foo'\}.stuff\(\) +<<< .*tests/test_aspectlib_debug.py:\d+:test_attributes.*\n\{test_aspectlib_debug.OldStuff foo='bar' bar='foo'\}.stuff => bar\n$", buf.getvalue()) MyStuff('bar').stuff() - assert re.match("^\{test_aspectlib_debug.OldStuff foo='bar' bar='foo'\}.stuff\(\) +<<< .*tests/test_aspectlib_debug.py:\d+:test_attributes.*\n\{test_aspectlib_debug.OldStuff foo='bar' bar='foo'\}.stuff => bar\n$", buf.getvalue()) + assert re.match(r"^\{test_aspectlib_debug.OldStuff foo='bar' bar='foo'\}.stuff\(\) +<<< .*tests/test_aspectlib_debug.py:\d+:test_attributes.*\n\{test_aspectlib_debug.OldStuff foo='bar' bar='foo'\}.stuff => bar\n$", buf.getvalue()) def test_no_stack_old_style(): From f4fe0d74effbd308a22281bf8d939dfb976dd6f3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ionel=20Cristian=20M=C4=83rie=C8=99?= Date: Tue, 25 Nov 2014 15:43:47 +0200 Subject: [PATCH 065/179] Update changelog. --- docs/changelog.rst | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/docs/changelog.rst b/docs/changelog.rst index e844054..7622dfe 100644 --- a/docs/changelog.rst +++ b/docs/changelog.rst @@ -1,10 +1,15 @@ Changelog ========= +Version 1.2.2 +------------- + +* Add support for weakrefs in the ``__logged__`` wrapper from :obj:`aspectlib.debug.log` decorator. + Version 1.2.1 ------------- -* Don't raise exceptions from Replay.__exit__ if there would be an error (makes original cause hard to debug). +* Don't raise exceptions from ``Replay.__exit__`` if there would be an error (makes original cause hard to debug). Version 1.2.0 ------------- From 8a07ee4b6a60ed97794d649bc135aa70d7049838 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ionel=20Cristian=20M=C4=83rie=C8=99?= Date: Tue, 25 Nov 2014 15:43:54 +0200 Subject: [PATCH 066/179] =?UTF-8?q?Bump=20version:=201.2.1=20=E2=86=92=201?= =?UTF-8?q?.2.2?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .bumpversion.cfg | 2 +- setup.py | 2 +- src/aspectlib/__init__.py | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/.bumpversion.cfg b/.bumpversion.cfg index 0319bff..8243127 100644 --- a/.bumpversion.cfg +++ b/.bumpversion.cfg @@ -1,5 +1,5 @@ [bumpversion] -current_version = 1.2.1 +current_version = 1.2.2 files = setup.py src/aspectlib/__init__.py commit = True tag = False diff --git a/setup.py b/setup.py index 0f738da..4936fe6 100644 --- a/setup.py +++ b/setup.py @@ -19,7 +19,7 @@ def read(*names, **kwargs): setup( name="aspectlib", - version="1.2.1", + version="1.2.2", url='https://github.com/ionelmc/python-aspectlib', download_url='', license='BSD', diff --git a/src/aspectlib/__init__.py b/src/aspectlib/__init__.py index cd4bd0a..9238e43 100644 --- a/src/aspectlib/__init__.py +++ b/src/aspectlib/__init__.py @@ -40,7 +40,7 @@ __all__ = 'weave', 'Aspect', 'Proceed', 'Return', 'ALL_METHODS', 'NORMAL_METHODS' -__version__ = '1.2.1' +__version__ = '1.2.2' logger = getLogger(__name__) logdebug = logf(logger.debug) From c1a9e26b13f88426db68f9da6960934a2cd2536a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ionel=20Cristian=20M=C4=83rie=C8=99?= Date: Wed, 24 Dec 2014 21:15:52 +0200 Subject: [PATCH 067/179] Update the intro. --- README.rst | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/README.rst b/README.rst index 53218ca..c4ca13a 100644 --- a/README.rst +++ b/README.rst @@ -25,7 +25,9 @@ python-aspectlib ``aspectlib`` is an aspect-oriented programming, monkey-patch and decorators library. It is useful when changing -behavior in existing code is desired. +behavior in existing code is desired. It includes tools for debugging and testing: simple mock/record and a complete +capture/replay framework. + Documentation ============= From 2800ff5c37afbfab6b6177f6a9fc923fc2edba29 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ionel=20Cristian=20M=C4=83rie=C8=99?= Date: Wed, 24 Dec 2014 21:22:26 +0200 Subject: [PATCH 068/179] Update description. --- setup.py | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/setup.py b/setup.py index 4936fe6..54c90ba 100644 --- a/setup.py +++ b/setup.py @@ -23,7 +23,9 @@ def read(*names, **kwargs): url='https://github.com/ionelmc/python-aspectlib', download_url='', license='BSD', - description="Aspect-Oriented Programming toolkit.", + description="aspectlib is an aspect-oriented programming, monkey-patch and decorators library. It is useful when " + "changing behavior in existing code is desired. It includes tools for debugging and testing: simple " + "mock/record and a complete capture/replay framework.", long_description="%s\n%s" % (read('README.rst'), re.sub(':obj:`~?(.*?)`', r'``\1``', read('docs', 'changelog.rst'))), author='Ionel Cristian Mărieș', author_email='contact@ionelmc.ro', @@ -47,8 +49,9 @@ def read(*names, **kwargs): 'Programming Language :: Python :: Implementation :: PyPy', ], keywords=[ - 'python', 'aop', 'aspects', 'aspect oriented programming', 'decorators', 'patch', 'monkeypatch', 'weave', - 'debug', 'log', 'tests', 'mock' + 'aop', 'aspects', 'aspect oriented programming', 'decorators', 'patch', 'monkeypatch', 'weave', 'debug', 'log', + 'tests', 'mock', 'capture', 'replay', 'capture-replay', 'debugging', 'patching', 'monkeypatching', 'record', + 'recording', 'mocking', 'logger' ], install_requires=[ ], From e3ca04ab58456691302f598ecd1c23ddbf1d0ef5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ionel=20Cristian=20M=C4=83rie=C8=99?= Date: Wed, 15 Oct 2014 16:52:09 +0300 Subject: [PATCH 069/179] Experiment. --- src/aspectlib/contrib.py | 40 ++++++++++++++++++++++++++++++++++++++++ 1 file changed, 40 insertions(+) create mode 100644 src/aspectlib/contrib.py diff --git a/src/aspectlib/contrib.py b/src/aspectlib/contrib.py new file mode 100644 index 0000000..073a972 --- /dev/null +++ b/src/aspectlib/contrib.py @@ -0,0 +1,40 @@ +from logging import getLogger +from time import sleep + +from aspectlib import Aspect + +logger = getLogger(__name__) + + +def retry(func=None, retries=5, backoff=None, exceptions=(IOError, OSError, EOFError), cleanup=None, sleep=sleep): + """ + Decorator that retries the call ``retries`` times if ``func`` raises ``exceptions``. Can use a ``backoff`` function + to sleep till next retry. + + Example:: + + >>> + """ + @Aspect(bind=True) + def Retry(cutpoint, *args, **kwargs): + for count in range(retries + 1): + try: + if count and cleanup: + cleanup() + yield + except exceptions: + if count == retries: + raise + if not backoff: + timeout = 0 + elif isinstance(backoff, (int, float)): + timeout = backoff + else: + timeout = backoff(count) + logger.exception("%s(%s, %s) raised exception. %s retries left. Sleeping %s secs.", + cutpoint.__name__, args, kwargs, retries - count, timeout) + sleep(timeout) + return Retry if func is None else Retry(func) + +retry.exponential_backoff = lambda count: 2 ** count +retry.flat_backoff = lambda count: (1, 5)[count] if count < 2 else 15*2**count From 20bdbb89f57bc781c51e3b848d90c62b2e089685 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ionel=20Cristian=20M=C4=83rie=C8=99?= Date: Tue, 24 Feb 2015 01:19:47 +0200 Subject: [PATCH 070/179] Move templates around. --- {conf => ci/templates}/.travis.yml | 0 {conf => ci/templates}/appveyor.yml | 0 {conf => ci/templates}/tox.ini | 0 3 files changed, 0 insertions(+), 0 deletions(-) rename {conf => ci/templates}/.travis.yml (100%) rename {conf => ci/templates}/appveyor.yml (100%) rename {conf => ci/templates}/tox.ini (100%) diff --git a/conf/.travis.yml b/ci/templates/.travis.yml similarity index 100% rename from conf/.travis.yml rename to ci/templates/.travis.yml diff --git a/conf/appveyor.yml b/ci/templates/appveyor.yml similarity index 100% rename from conf/appveyor.yml rename to ci/templates/appveyor.yml diff --git a/conf/tox.ini b/ci/templates/tox.ini similarity index 100% rename from conf/tox.ini rename to ci/templates/tox.ini From 42e2800034c0374405f2eea533f0f125c220edff Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ionel=20Cristian=20M=C4=83rie=C8=99?= Date: Tue, 24 Feb 2015 01:20:16 +0200 Subject: [PATCH 071/179] Convert to napoleon docstrings. --- src/aspectlib/__init__.py | 46 +++++++++---------- src/aspectlib/debug.py | 72 +++++++++++++++--------------- src/aspectlib/test.py | 94 ++++++++++++++++++++------------------- 3 files changed, 107 insertions(+), 105 deletions(-) diff --git a/src/aspectlib/__init__.py b/src/aspectlib/__init__.py index 9238e43..41cf169 100644 --- a/src/aspectlib/__init__.py +++ b/src/aspectlib/__init__.py @@ -349,31 +349,27 @@ def weave(target, aspects, **options): """ Send a message to a recipient - :param target: The object to weave. - :type target: string, class, instance, function or builtin - - :param aspects: The aspects to apply to the object. - :type target: :py:obj:`aspectlib.Aspect`, function decorator or list of - - :param bool subclasses: - If ``True``, subclasses of target are weaved. *Only available for classes* - - :param bool aliases: - If ``True``, aliases of target are replaced. - - :param bool lazy: - If ``True`` only target's ``__init__`` method is patched, the rest of the methods are patched after ``__init__`` - is called. *Only available for classes*. - - :param methods: Methods from target to patch. *Only available for classes* - :type methods: list or regex or string - - :returns: - :class:`aspectlib.Rollback` instance - - :raises TypeError: - If target is a unacceptable object, or the specified options are not available for that type of object. - + Args: + target (string, class, instance, function or builtin): + The object to weave. + aspects (:py:obj:`aspectlib.Aspect`, function decorator or list of): + The aspects to apply to the object. + subclasses (bool): + If ``True``, subclasses of target are weaved. *Only available for classes* + aliases (bool): + If ``True``, aliases of target are replaced. + lazy (bool): + If ``True`` only target's ``__init__`` method is patched, the rest of the methods are patched after + ``__init__`` is called. *Only available for classes*. + methods (list or regex or string): + Methods from target to patch. *Only available for classes* + + Returns: + aspectlib.Rollback: An object that can rollback the patches. + + Raises: + TypeError: If target is a unacceptable object, or the specified options are not available for that type of + object. .. versionchanged:: 0.4.0 diff --git a/src/aspectlib/debug.py b/src/aspectlib/debug.py index 48a33bd..2ee6225 100644 --- a/src/aspectlib/debug.py +++ b/src/aspectlib/debug.py @@ -62,41 +62,43 @@ def log(func=None, """ Decorates `func` to have logging. - :param function func: - Function to decorate. If missing log returns a partial which you can use as a decorator. - :param int stacktrace: - Number of frames to show. - :param int stacktrace_align: - Column to align the framelist to. - :param list attributes: - List of instance attributes to show, in case the function is a instance method. - :param bool module: - Show the module. - :param bool call: - If ``True``, then show calls. If ``False`` only show the call details on exceptions (if ``exception`` is - enabled) (default: ``True``) - :param bool call_args: - If ``True``, then show call arguments. (default: ``True``) - :param bool call_args_repr: - Function to convert one argument to a string. (default: ``repr``) - :param bool result: - If ``True``, then show result. (default: ``True``) - :param bool exception: - If ``True``, then show exceptions. (default: ``True``) - :param function exception_repr: - Function to convert an exception to a string. (default: ``repr``) - :param function result_repr: - Function to convert the result object to a string. (default: ``strip_non_ascii`` - like ``str`` but nonascii - characters are replaced with dots.) - :param string use_logging: - Emit log messages with the given loglevel. (default: ``"CRITICAL"``) - :param fileobject print_to: - File object to write to, in case you don't want to use logging module. (default: ``None`` - printing is - disabled) - - :returns: A decorator or a wrapper. - - Example:: + Args + func (function): + Function to decorate. If missing log returns a partial which you can use as a decorator. + stacktrace (int): + Number of frames to show. + stacktrace_align (int): + Column to align the framelist to. + attributes (list): + List of instance attributes to show, in case the function is a instance method. + module (bool): + Show the module. + call (bool): + If ``True``, then show calls. If ``False`` only show the call details on exceptions (if ``exception`` is + enabled) (default: ``True``) + call_args (bool): + If ``True``, then show call arguments. (default: ``True``) + call_args_repr (bool): + Function to convert one argument to a string. (default: ``repr``) + result (bool): + If ``True``, then show result. (default: ``True``) + exception (bool): + If ``True``, then show exceptions. (default: ``True``) + exception_repr (function): + Function to convert an exception to a string. (default: ``repr``) + result_repr (function): + Function to convert the result object to a string. (default: ``strip_non_ascii`` - like ``str`` but nonascii + characters are replaced with dots.) + use_logging (string): + Emit log messages with the given loglevel. (default: ``"CRITICAL"``) + print_to (fileobject): + File object to write to, in case you don't want to use logging module. (default: ``None`` - printing is + disabled) + + Returns: + A decorator or a wrapper. + + Example: >>> @log(print_to=sys.stdout) ... def a(weird=False): diff --git a/src/aspectlib/test.py b/src/aspectlib/test.py index 31f48a1..74d0a71 100644 --- a/src/aspectlib/test.py +++ b/src/aspectlib/test.py @@ -52,9 +52,12 @@ def mock(return_value, call=False): """ Factory for a decorator that makes the function return a given `return_value`. - :param return_value: Value to return from the wrapper. - :param bool call: If ``True``, call the decorated function. (default: ``False``) - :returns: A decorator. + Args: + return_value: Value to return from the wrapper. + call (bool): If ``True``, call the decorated function. (default: ``False``) + + Returns: + A decorator. """ def mock_decorator(func): @@ -224,24 +227,24 @@ def record(func=None, recurse_lock_factory=allocate_lock, **options): """ Factory or decorator (depending if `func` is initially given). - :param list callback: - An a callable that is to be called with ``instance, function, args, kwargs``. - :param list calls: - An object where the `Call` objects are appended. If not given and ``callback`` is not specified then a new list - object will be created. - :param bool iscalled: - If ``True`` the `func` will be called. (default: ``False``) - :param bool extended: - If ``True`` the `func`'s ``__name__`` will also be included in the call list. (default: ``False``) - :param bool results: - If ``True`` the results (and exceptions) will also be included in the call list. (default: ``False``) - :returns: - A wrapper that has a `calls` property. - - The decorator returns a wrapper that records all calls made to `func`. The history is available as a ``call`` - property. If access to the function is too hard then you need to specify the history manually. - - Example:: + Args: + callback (list): + An a callable that is to be called with ``instance, function, args, kwargs``. + calls (list): + An object where the `Call` objects are appended. If not given and ``callback`` is not specified then a new list + object will be created. + iscalled (bool): + If ``True`` the `func` will be called. (default: ``False``) + extended (bool): + If ``True`` the `func`'s ``__name__`` will also be included in the call list. (default: ``False``) + results (bool): + If ``True`` the results (and exceptions) will also be included in the call list. (default: ``False``) + + Returns: + A wrapper that records all calls made to `func`. The history is available as a ``call`` + property. If access to the function is too hard then you need to specify the history manually. + + Example: >>> @record ... def a(x, y, a, b): @@ -420,19 +423,17 @@ class Story(_RecordingBase): This a simple yet flexible tool that can do "capture-replay mocking" or "test doubles" [1]_. It leverages ``aspectlib``'s powerful :obj:`weaver `. - :param target: - Targets to weave in the `story`/`replay` transactions. - :type target: - Same as for :obj:`aspectlib.weave`. - :param bool subclasses: - If ``True``, subclasses of target are weaved. *Only available for classes* - :param bool aliases: - If ``True``, aliases of target are replaced. - :param bool lazy: - If ``True`` only target's ``__init__`` method is patched, the rest of the methods are patched after ``__init__`` - is called. *Only available for classes*. - :param methods: Methods from target to patch. *Only available for classes* - :type methods: list or regex or string + Args: + target (same as for :obj:`aspectlib.weave`): + Targets to weave in the `story`/`replay` transactions. + subclasses (bool): + If ``True``, subclasses of target are weaved. *Only available for classes* + aliases (bool): + If ``True``, aliases of target are replaced. + lazy (bool): + If ``True`` only target's ``__init__`` method is patched, the rest of the methods are patched after ``__init__`` + is called. *Only available for classes*. + methods (list or regex or string): Methods from target to patch. *Only available for classes* The ``Story`` allows some testing patterns that are hard to do with other tools: @@ -468,17 +469,20 @@ def __init__(self, *args, **kwargs): def replay(self, **options): """ - :param bool proxy: - If ``True`` then unexpected uses are allowed (will use the real functions) but they are collected for later - use. Default: ``True``. - :param bool strict: - If ``True`` then an ``AssertionError`` is raised when there were `unexpected calls` or there were `missing - calls` (specified in the story but not called). Default: ``True``. - :param bool dump: - If ``True`` then the `unexpected`/`missing calls` will be printed (to ``sys.stdout``). Default: ``True``. - :returns: A :obj:`aspectlib.test.Replay` object. - - Example:: + Args: + proxy (bool): + If ``True`` then unexpected uses are allowed (will use the real functions) but they are collected for later + use. Default: ``True``. + strict (bool): + If ``True`` then an ``AssertionError`` is raised when there were `unexpected calls` or there were `missing + calls` (specified in the story but not called). Default: ``True``. + dump (bool): + If ``True`` then the `unexpected`/`missing calls` will be printed (to ``sys.stdout``). Default: ``True``. + + Returns: + A :obj:`aspectlib.test.Replay` object. + + Example: >>> import mymod >>> with Story(mymod) as story: From faf966369cf8d666726c90be5a9544de3aa6ad60 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ionel=20Cristian=20M=C4=83rie=C8=99?= Date: Tue, 24 Feb 2015 01:20:26 +0200 Subject: [PATCH 072/179] Whatever --- python-aspectlib.komodoproject | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/python-aspectlib.komodoproject b/python-aspectlib.komodoproject index ce2a5d3..9a00932 100644 --- a/python-aspectlib.komodoproject +++ b/python-aspectlib.komodoproject @@ -9,10 +9,10 @@ 1 1 1 - .tox/3.3/bin/python3.3 - .tox/3.3/lib/python3.3:/home/ionel/osp/python-aspectlib/.tox/3.3/lib/python3.3/site-packages - .tox/2.7/bin/python2.7 - .tox/2.7/lib/python2.7:/home/ionel/osp/python-aspectlib/.tox/2.7/lib/python2.7/site-packages:/usr/lib/python2.7 + .tox/3.4-nocover/bin/python3.4 + src + .tox/2.7-nocover/bin/python2.7 + src From 81f6dc9a7d003652d072f600e237922434520be6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ionel=20Cristian=20M=C4=83rie=C8=99?= Date: Tue, 24 Feb 2015 01:39:28 +0200 Subject: [PATCH 073/179] Update skel. --- .bumpversion.cfg | 2 +- .coveragerc | 1 + .gitignore | 4 +- .isort.cfg | 6 --- .travis.yml | 1 + MANIFEST.in | 3 +- README.rst | 47 ++++++++++++++----- appveyor.yml | 58 +++++++++++++++++------ ci/appveyor-bootstrap.ps1 | 88 +++++++++++++++++++++++++++++++++++ ci/appveyor-with-compiler.cmd | 37 +++++++++++++++ ci/bootstrap.py | 60 ++++++++++++++++++++++++ ci/templates/.travis.yml | 1 + ci/templates/appveyor.yml | 31 +++++++----- ci/templates/tox.ini | 12 +++-- docs/conf.py | 15 +++--- docs/contributing.rst | 1 + docs/index.rst | 3 +- setup.cfg | 11 ++++- setup.py | 23 +++++---- tox.ini | 40 ++++++---------- 20 files changed, 348 insertions(+), 96 deletions(-) delete mode 100644 .isort.cfg create mode 100644 ci/appveyor-bootstrap.ps1 create mode 100644 ci/appveyor-with-compiler.cmd create mode 100755 ci/bootstrap.py create mode 100644 docs/contributing.rst diff --git a/.bumpversion.cfg b/.bumpversion.cfg index 8243127..680c886 100644 --- a/.bumpversion.cfg +++ b/.bumpversion.cfg @@ -1,6 +1,6 @@ [bumpversion] current_version = 1.2.2 -files = setup.py src/aspectlib/__init__.py +files = setup.py docs/conf.py src/aspectlib/__init__.py commit = True tag = False diff --git a/.coveragerc b/.coveragerc index fef4a54..e0e7f76 100644 --- a/.coveragerc +++ b/.coveragerc @@ -2,6 +2,7 @@ source = src [run] +branch = True source = src parallel = true diff --git a/.gitignore b/.gitignore index 23606f4..fc6d847 100644 --- a/.gitignore +++ b/.gitignore @@ -36,6 +36,7 @@ htmlcov .mr.developer.cfg .project .pydevproject +.idea # Complexity output/*.html @@ -49,4 +50,5 @@ docs/_build .*.sw[po] .build .ve -.bootstrap \ No newline at end of file +.bootstrap +*.bak diff --git a/.isort.cfg b/.isort.cfg deleted file mode 100644 index 6692643..0000000 --- a/.isort.cfg +++ /dev/null @@ -1,6 +0,0 @@ -[settings] -force_single_line=True -line_length=120 -known_first_party={{ cookiecutter.package_name }} -default_section=THIRDPARTY -forced_separate=test_{{ cookiecutter.package_name }} diff --git a/.travis.yml b/.travis.yml index 2f2dd13..aac1956 100644 --- a/.travis.yml +++ b/.travis.yml @@ -1,5 +1,6 @@ language: python python: 2.7 +sudo: false env: global: LD_PRELOAD=/lib/x86_64-linux-gnu/libSegFault.so diff --git a/MANIFEST.in b/MANIFEST.in index 6112c1c..3af7c27 100644 --- a/MANIFEST.in +++ b/MANIFEST.in @@ -1,7 +1,7 @@ graft docs graft examples -graft conf graft src +graft ci graft tests include *.komodoproject @@ -16,7 +16,6 @@ include CONTRIBUTING.rst include LICENSE include README.rst -include bootstrap.py include tox.ini .travis.yml appveyor.yml include conftest.py diff --git a/README.rst b/README.rst index c4ca13a..e55dfc7 100644 --- a/README.rst +++ b/README.rst @@ -2,27 +2,52 @@ python-aspectlib ================ -+--------------------+--------------------+---------------------+ -| | |travis-badge| | | | | |version-badge| | -| | |appveyor-badge| | | |coverage-badge| | | |downloads-badge| | -+--------------------+--------------------+---------------------+ +| |docs| |travis| |appveyor| |coveralls| |landscape| |scrutinizer| +| |version| |downloads| |wheel| |supported-versions| |supported-implementations| -.. |travis-badge| image:: http://img.shields.io/travis/ionelmc/python-aspectlib.png?style=flat +.. |docs| image:: https://readthedocs.org/projects/python-aspectlib/badge/?style=flat + :target: https://readthedocs.org/projects/python-aspectlib + :alt: Documentation Status + +.. |travis| image:: http://img.shields.io/travis/ionelmc/python-aspectlib/master.png?style=flat :alt: Travis-CI Build Status :target: https://travis-ci.org/ionelmc/python-aspectlib -.. |appveyor-badge| image:: https://ci.appveyor.com/api/projects/status/u2f05p7rmd5hsixi + +.. |appveyor| image:: https://ci.appveyor.com/api/projects/status/github/ionelmc/python-aspectlib?branch=master :alt: AppVeyor Build Status :target: https://ci.appveyor.com/project/ionelmc/python-aspectlib -.. |coverage-badge| image:: http://img.shields.io/coveralls/ionelmc/python-aspectlib.png?style=flat + +.. |coveralls| image:: http://img.shields.io/coveralls/ionelmc/python-aspectlib/master.png?style=flat :alt: Coverage Status :target: https://coveralls.io/r/ionelmc/python-aspectlib -.. |version-badge| image:: http://img.shields.io/pypi/v/aspectlib.png?style=flat - :alt: PYPI Package + +.. |landscape| image:: https://landscape.io/github/ionelmc/python-aspectlib/master/landscape.svg?style=flat + :target: https://landscape.io/github/ionelmc/python-aspectlib/master + :alt: Code Quality Status + +.. |version| image:: http://img.shields.io/pypi/v/aspectlib.png?style=flat + :alt: PyPI Package latest release + :target: https://pypi.python.org/pypi/aspectlib + +.. |downloads| image:: http://img.shields.io/pypi/dm/aspectlib.png?style=flat + :alt: PyPI Package monthly downloads + :target: https://pypi.python.org/pypi/aspectlib + +.. |wheel| image:: https://pypip.in/wheel/aspectlib/badge.png?style=flat + :alt: PyPI Wheel :target: https://pypi.python.org/pypi/aspectlib -.. |downloads-badge| image:: http://img.shields.io/pypi/dm/aspectlib.png?style=flat - :alt: PYPI Package + +.. |supported-versions| image:: https://pypip.in/py_versions/aspectlib/badge.png?style=flat + :alt: Supported versions + :target: https://pypi.python.org/pypi/aspectlib + +.. |supported-implementations| image:: https://pypip.in/implementation/aspectlib/badge.png?style=flat + :alt: Supported imlementations :target: https://pypi.python.org/pypi/aspectlib +.. |scrutinizer| image:: https://img.shields.io/scrutinizer/g/ionelmc/python-aspectlib/master.png?style=flat + :alt: Scrtinizer Status + :target: https://scrutinizer-ci.com/g/ionelmc/python-aspectlib/ ``aspectlib`` is an aspect-oriented programming, monkey-patch and decorators library. It is useful when changing behavior in existing code is desired. It includes tools for debugging and testing: simple mock/record and a complete diff --git a/appveyor.yml b/appveyor.yml index 91405dd..424e226 100644 --- a/appveyor.yml +++ b/appveyor.yml @@ -1,26 +1,54 @@ -build: false +version: '{build}' +build: off environment: matrix: - TOXENV: check + PYTHON_HOME: "C:\\Python27" + PYTHON_VERSION: "2.7" + PYTHON_ARCH: "32" - TOXENV: "2.7" + TOXPYTHON: "C:\\Python27\\python.exe" + WINDOWS_SDK_VERSION: "v7.0" + PYTHON_HOME: "C:\\Python27" + PYTHON_VERSION: "2.7" + PYTHON_ARCH: "32" - TOXENV: "2.7-debug" - - TOXENV: "2.7-nocover" - - TOXENV: "2.7-nocover-debug" + TOXPYTHON: "C:\\Python27\\python.exe" + WINDOWS_SDK_VERSION: "v7.0" + PYTHON_HOME: "C:\\Python27" + PYTHON_VERSION: "2.7" + PYTHON_ARCH: "32" + - TOXENV: "3.3" + TOXPYTHON: "C:\\Python33\\python.exe" + WINDOWS_SDK_VERSION: "v7.1" + PYTHON_HOME: "C:\\Python33" + PYTHON_VERSION: "3.3" + PYTHON_ARCH: "32" + - TOXENV: "3.3-debug" + TOXPYTHON: "C:\\Python33\\python.exe" + WINDOWS_SDK_VERSION: "v7.1" + PYTHON_HOME: "C:\\Python33" + PYTHON_VERSION: "3.3" + PYTHON_ARCH: "32" - TOXENV: "3.4" + TOXPYTHON: "C:\\Python34\\python.exe" + WINDOWS_SDK_VERSION: "v7.1" + PYTHON_HOME: "C:\\Python34" + PYTHON_VERSION: "3.4" + PYTHON_ARCH: "32" - TOXENV: "3.4-debug" - - TOXENV: "3.4-nocover" - - TOXENV: "3.4-nocover-debug" + TOXPYTHON: "C:\\Python34\\python.exe" + WINDOWS_SDK_VERSION: "v7.1" + PYTHON_HOME: "C:\\Python34" + PYTHON_VERSION: "3.4" + PYTHON_ARCH: "32" init: - "ECHO %TOXENV%" - - ps: "ls C:/Python*" + - ps: "ls C:\\Python*" install: - - ps: Invoke-WebRequest "https://bootstrap.pypa.io/ez_setup.py" -OutFile "c:/ez_setup.py" - - ps: Invoke-WebRequest "https://bootstrap.pypa.io/get-pip.py" -OutFile "c:/get-pip.py" - - "c:/python27/python c:/ez_setup.py" - - "c:/python27/python c:/get-pip.py" - - "c:/python27/Scripts/pip install tox" + - "powershell ci\\appveyor-bootstrap.ps1" test_script: - - "c:/python27/Scripts/tox --version" - - "c:/python27/Scripts/virtualenv --version" - - "c:/python27/Scripts/pip --version" - - "c:/python27/Scripts/tox" + - "%PYTHON_HOME%\\Scripts\\tox --version" + - "%PYTHON_HOME%\\Scripts\\virtualenv --version" + - "%PYTHON_HOME%\\Scripts\\pip --version" + - "%PYTHON_HOME%\\Scripts\\tox" diff --git a/ci/appveyor-bootstrap.ps1 b/ci/appveyor-bootstrap.ps1 new file mode 100644 index 0000000..1dd5342 --- /dev/null +++ b/ci/appveyor-bootstrap.ps1 @@ -0,0 +1,88 @@ +# Source: https://github.com/pypa/python-packaging-user-guide/blob/master/source/code/install.ps1 +# Sample script to install Python and pip under Windows +# Authors: Olivier Grisel and Kyle Kastner +# License: CC0 1.0 Universal: http://creativecommons.org/publicdomain/zero/1.0/ + +$BASE_URL = "https://www.python.org/ftp/python/" +$GET_PIP_URL = "https://bootstrap.pypa.io/get-pip.py" +$GET_PIP_PATH = "C:\get-pip.py" + + +function DownloadPython ($python_version, $platform_suffix) { + $webclient = New-Object System.Net.WebClient + $filename = "python-" + $python_version + $platform_suffix + ".msi" + $url = $BASE_URL + $python_version + "/" + $filename + + $basedir = $pwd.Path + "\" + $filepath = $basedir + $filename + if (Test-Path $filename) { + Write-Host "Reusing" $filepath + return $filepath + } + + # Download and retry up to 5 times in case of network transient errors. + Write-Host "Downloading" $filename "from" $url + $retry_attempts = 3 + for($i=0; $i -lt $retry_attempts; $i++){ + try { + $webclient.DownloadFile($url, $filepath) + break + } + Catch [Exception]{ + Start-Sleep 1 + } + } + Write-Host "File saved at" $filepath + return $filepath +} + + +function InstallPython ($python_version, $architecture, $python_home) { + Write-Host "Installing Python" $python_version "for" $architecture "bit architecture to" $python_home + if (Test-Path $python_home) { + Write-Host $python_home "already exists, skipping." + return $false + } + if ($architecture -eq "32") { + $platform_suffix = "" + } else { + $platform_suffix = ".amd64" + } + $filepath = DownloadPython $python_version $platform_suffix + Write-Host "Installing" $filepath "to" $python_home + $args = "/qn /i $filepath TARGETDIR=$python_home" + Write-Host "msiexec.exe" $args + Start-Process -FilePath "msiexec.exe" -ArgumentList $args -Wait -Passthru + Write-Host "Python $python_version ($architecture) installation complete" + return $true +} + + +function InstallPip ($python_home) { + $pip_path = $python_home + "/Scripts/pip.exe" + $python_path = $python_home + "/python.exe" + if (-not(Test-Path $pip_path)) { + Write-Host "Installing pip..." + $webclient = New-Object System.Net.WebClient + $webclient.DownloadFile($GET_PIP_URL, $GET_PIP_PATH) + Write-Host "Executing:" $python_path $GET_PIP_PATH + Start-Process -FilePath "$python_path" -ArgumentList "$GET_PIP_PATH" -Wait -Passthru + } else { + Write-Host "pip already installed." + } +} + +function InstallPackage ($python_home, $pkg) { + $pip_path = $python_home + "/Scripts/pip.exe" + & $pip_path install $pkg +} + +function main () { + InstallPython $env:PYTHON_VERSION $env:PYTHON_ARCH $env:PYTHON_HOME + InstallPip $env:PYTHON_HOME + InstallPackage $env:PYTHON_HOME setuptools + InstallPackage $env:PYTHON_HOME wheel + InstallPackage $env:PYTHON_HOME tox +} + +main diff --git a/ci/appveyor-with-compiler.cmd b/ci/appveyor-with-compiler.cmd new file mode 100644 index 0000000..3619733 --- /dev/null +++ b/ci/appveyor-with-compiler.cmd @@ -0,0 +1,37 @@ +:: To build extensions for 64 bit Python 3, we need to configure environment +:: variables to use the MSVC 2010 C++ compilers from GRMSDKX_EN_DVD.iso of: +:: MS Windows SDK for Windows 7 and .NET Framework 4 (SDK v7.1) +:: +:: To build extensions for 64 bit Python 2, we need to configure environment +:: variables to use the MSVC 2008 C++ compilers from GRMSDKX_EN_DVD.iso of: +:: MS Windows SDK for Windows 7 and .NET Framework 3.5 (SDK v7.0) +:: +:: 32 bit builds do not require specific environment configurations. +:: +:: Note: this script needs to be run with the /E:ON and /V:ON flags for the +:: cmd interpreter, at least for (SDK v7.0) +:: +:: More details at: +:: https://github.com/cython/cython/wiki/64BitCythonExtensionsOnWindows +:: http://stackoverflow.com/a/13751649/163740 +:: +:: Author: Olivier Grisel +:: License: CC0 1.0 Universal: http://creativecommons.org/publicdomain/zero/1.0/ +@ECHO OFF + +SET COMMAND_TO_RUN=%* +SET WIN_SDK_ROOT=C:\Program Files\Microsoft SDKs\Windows + +IF "%PYTHON_ARCH%"=="64" ( + ECHO SDK: %WINDOWS_SDK_VERSION% ARCH: %PYTHON_ARCH% + SET DISTUTILS_USE_SDK=1 + SET MSSdk=1 + "%WIN_SDK_ROOT%\%WINDOWS_SDK_VERSION%\Setup\WindowsSdkVer.exe" -q -version:%WINDOWS_SDK_VERSION% + "%WIN_SDK_ROOT%\%WINDOWS_SDK_VERSION%\Bin\SetEnv.cmd" /x64 /release + ECHO Executing: %COMMAND_TO_RUN% + call %COMMAND_TO_RUN% || EXIT 1 +) ELSE ( + ECHO SDK: %WINDOWS_SDK_VERSION% ARCH: %PYTHON_ARCH% + ECHO Executing: %COMMAND_TO_RUN% + call %COMMAND_TO_RUN% || EXIT 1 +) diff --git a/ci/bootstrap.py b/ci/bootstrap.py new file mode 100755 index 0000000..cf93a54 --- /dev/null +++ b/ci/bootstrap.py @@ -0,0 +1,60 @@ +#!/usr/bin/env python +# -*- coding: utf-8 -*- +from __future__ import absolute_import, print_function, unicode_literals + +import os +import sys +from os.path import exists +from os.path import join + + +if __name__ == "__main__": + base_path = join(".tox", "configure") + if sys.platform == "win32": + bin_path = join(base_path, "Scripts") + else: + bin_path = join(base_path, "bin") + if not exists(base_path): + import subprocess + print("Bootstrapping ...") + try: + subprocess.check_call(["virtualenv", base_path]) + except Exception: + subprocess.check_call([sys.executable, "-m", "virtualenv", base_path]) + print("Installing `jinja2` and `matrix` into bootstrap environment ...") + subprocess.check_call([join(bin_path, "pip"), "install", "jinja2", "matrix"]) + activate = join(bin_path, "activate_this.py") + exec(compile(open(activate, "rb").read(), activate, "exec"), dict(__file__=activate)) + + import jinja2 + import matrix + + jinja = jinja2.Environment( + loader=jinja2.FileSystemLoader(join("ci", "templates")), + trim_blocks=True, + lstrip_blocks=True, + keep_trailing_newline=True + ) + tox_environments = {} + for (alias, conf) in matrix.from_file("setup.cfg").items(): + python = conf["python_versions"] + deps = conf["dependencies"] + if "coverage_flags" in conf: + cover = {"false": False, "true": True}[conf["coverage_flags"].lower()] + if "environment_variables" in conf: + env_vars = conf["environment_variables"] + + tox_environments[alias] = { + "python": "python" + python if "py" not in python else python, + "deps": deps.split(), + } + if "coverage_flags" in conf: + tox_environments[alias].update(cover=cover) + if "environment_variables" in conf: + tox_environments[alias].update(env_vars=env_vars.split()) + + for name in os.listdir(join("ci", "templates")): + with open(name, "w") as fh: + fh.write(jinja.get_template(name).render(tox_environments=tox_environments)) + print("Wrote {}".format(name)) + print("DONE.") diff --git a/ci/templates/.travis.yml b/ci/templates/.travis.yml index b295333..684b18d 100644 --- a/ci/templates/.travis.yml +++ b/ci/templates/.travis.yml @@ -1,5 +1,6 @@ language: python python: 2.7 +sudo: false env: global: LD_PRELOAD=/lib/x86_64-linux-gnu/libSegFault.so diff --git a/ci/templates/appveyor.yml b/ci/templates/appveyor.yml index e5c93a2..cc03b27 100644 --- a/ci/templates/appveyor.yml +++ b/ci/templates/appveyor.yml @@ -1,21 +1,28 @@ -build: false +version: '{build}' +build: off environment: matrix: - TOXENV: check -{% for env, config in tox_environments|dictsort %}{% if '2.7' in env or '3.4' in env %} + PYTHON_HOME: "C:\\Python27" + PYTHON_VERSION: "2.7" + PYTHON_ARCH: "32" +{% for env, config in tox_environments|dictsort %}{% if env.startswith('2.7') or env.startswith('3.4') or env.startswith('3.3') %} + {% if config['cover'] %} - TOXENV: "{{ env }}" + TOXPYTHON: "C:\\Python{{ env[:3].replace('.', '') }}\\python.exe" + WINDOWS_SDK_VERSION: "v7.{{ '1' if env[0] == '3' else '0' }}" + PYTHON_HOME: "C:\\Python{{ env[:3].replace('.', '') }}" + PYTHON_VERSION: "{{ env[:3] }}" + PYTHON_ARCH: "32" + {% endif %} {% endif %}{% endfor %} init: - "ECHO %TOXENV%" - - ps: "ls C:/Python*" + - ps: "ls C:\\Python*" install: - - ps: Invoke-WebRequest "https://bootstrap.pypa.io/ez_setup.py" -OutFile "c:/ez_setup.py" - - ps: Invoke-WebRequest "https://bootstrap.pypa.io/get-pip.py" -OutFile "c:/get-pip.py" - - "c:/python27/python c:/ez_setup.py" - - "c:/python27/python c:/get-pip.py" - - "c:/python27/Scripts/pip install tox" + - "powershell ci\\appveyor-bootstrap.ps1" test_script: - - "c:/python27/Scripts/tox --version" - - "c:/python27/Scripts/virtualenv --version" - - "c:/python27/Scripts/pip --version" - - "c:/python27/Scripts/tox" + - "%PYTHON_HOME%\\Scripts\\tox --version" + - "%PYTHON_HOME%\\Scripts\\virtualenv --version" + - "%PYTHON_HOME%\\Scripts\\pip --version" + - "%PYTHON_HOME%\\Scripts\\tox" diff --git a/ci/templates/tox.ini b/ci/templates/tox.ini index 2aa5d2c..a0971a1 100644 --- a/ci/templates/tox.ini +++ b/ci/templates/tox.ini @@ -58,11 +58,11 @@ deps = docutils check-manifest flake8 - collective.checkdocs + readme + pygments usedevelop = true commands = - python setup.py checkdocs - python setup.py check --restructuredtext --strict --metadata + python setup.py check --strict --metadata --restructuredtext check-manifest {toxinidir} flake8 src @@ -102,11 +102,15 @@ setenv = WITH_COVERAGE=yes usedevelop = true commands = - {posargs:py.test --cov src -vv} + {posargs:py.test --cov=src --cov-report=term-missing -vv} {% endif %} +{% if config.cover or config.deps %} deps = {[testenv]deps} +{% endif %} +{% if config.cover %} pytest-cov +{% endif %} {% for dep in config.deps %} {{ dep }} {% endfor %} diff --git a/docs/conf.py b/docs/conf.py index 31b2dcc..a47e9fd 100644 --- a/docs/conf.py +++ b/docs/conf.py @@ -1,6 +1,9 @@ # -*- coding: utf-8 -*- -import re +from __future__ import unicode_literals + import os + + extensions = [ 'sphinx.ext.autodoc', 'sphinx.ext.autosummary', @@ -18,11 +21,10 @@ source_suffix = '.rst' master_doc = 'index' project = u'aspectlib' -copyright = u'2014, Ionel Cristian Mărieș' -version = release = re.findall( - 'version="(.*)"', - open(os.path.join(os.path.dirname(__file__), '../setup.py')).read() -)[0] +year = u'2015' +author = u'Ionel Cristian Maries' +copyright = '{0}, {1}'.format(year, author) +version = release = u'1.2.2' import sphinx_py3doc_enhanced_theme html_theme = "sphinx_py3doc_enhanced_theme" @@ -32,7 +34,6 @@ templates_path = ['.'] html_use_smartypants = True html_last_updated_fmt = '%b %d, %Y' -exclude_trees = ['pydoctheme'] html_split_index = True html_sidebars = { '**': ['searchbox.html', 'globaltoc.html', 'sourcelink.html'], diff --git a/docs/contributing.rst b/docs/contributing.rst new file mode 100644 index 0000000..e582053 --- /dev/null +++ b/docs/contributing.rst @@ -0,0 +1 @@ +.. include:: ../CONTRIBUTING.rst diff --git a/docs/index.rst b/docs/index.rst index 61ac5dc..72af676 100644 --- a/docs/index.rst +++ b/docs/index.rst @@ -5,7 +5,7 @@ Welcome to python-aspectlib's documentation! behavior in existing code is desired. .. toctree:: - :maxdepth: 1 + :maxdepth: 2 introduction installation @@ -16,6 +16,7 @@ behavior in existing code is desired. reference/index development todo + contributing changelog Indices and tables diff --git a/setup.cfg b/setup.cfg index 6f78ca0..f312f7a 100644 --- a/setup.cfg +++ b/setup.cfg @@ -1,6 +1,9 @@ [bdist_wheel] universal = 1 +[aliases] +release = register clean --all sdist bdist_wheel upload + [flake8] max-line-length = 140 exclude = tests/* @@ -23,7 +26,13 @@ addopts = --ignore bootstrap.py --doctest-modules --tb short - --cov-report term-missing + +[isort] +force_single_line=True +line_length=120 +known_first_party=aspectlib +default_section=THIRDPARTY +forced_separate=test_aspectlib [matrix] python_versions = diff --git a/setup.py b/setup.py index 54c90ba..9d50363 100644 --- a/setup.py +++ b/setup.py @@ -1,10 +1,14 @@ +#!/usr/bin/env python # -*- encoding: utf-8 -*- -import glob +from __future__ import absolute_import, print_function import io +import os import re +from glob import glob from os.path import basename from os.path import dirname from os.path import join +from os.path import relpath from os.path import splitext from setuptools import find_packages @@ -14,14 +18,12 @@ def read(*names, **kwargs): return io.open( join(dirname(__file__), *names), - encoding=kwargs.get("encoding", "utf8") + encoding=kwargs.get('encoding', 'utf8') ).read() setup( - name="aspectlib", - version="1.2.2", - url='https://github.com/ionelmc/python-aspectlib', - download_url='', + name='aspectlib', + version='1.2.2', license='BSD', description="aspectlib is an aspect-oriented programming, monkey-patch and decorators library. It is useful when " "changing behavior in existing code is desired. It includes tools for debugging and testing: simple " @@ -29,24 +31,29 @@ def read(*names, **kwargs): long_description="%s\n%s" % (read('README.rst'), re.sub(':obj:`~?(.*?)`', r'``\1``', read('docs', 'changelog.rst'))), author='Ionel Cristian Mărieș', author_email='contact@ionelmc.ro', + url='https://github.com/ionelmc/python-aspectlib', packages=find_packages('src'), package_dir={'': 'src'}, + py_modules=[splitext(basename(path))[0] for path in glob('src/*.py')], include_package_data=True, zip_safe=False, classifiers=[ - "Development Status :: 5 - Production/Stable", + # complete classifier list: http://pypi.python.org/pypi?%3Aaction=list_classifiers + 'Development Status :: 5 - Production/Stable', 'Intended Audience :: Developers', 'License :: OSI Approved :: BSD License', 'Operating System :: Unix', 'Operating System :: POSIX', 'Operating System :: Microsoft :: Windows', 'Programming Language :: Python', - 'Topic :: Utilities', 'Programming Language :: Python :: 2.6', 'Programming Language :: Python :: 2.7', 'Programming Language :: Python :: 3', + 'Programming Language :: Python :: 3.3', + 'Programming Language :: Python :: 3.4', 'Programming Language :: Python :: Implementation :: CPython', 'Programming Language :: Python :: Implementation :: PyPy', + 'Topic :: Utilities', ], keywords=[ 'aop', 'aspects', 'aspect oriented programming', 'decorators', 'patch', 'monkeypatch', 'weave', 'debug', 'log', diff --git a/tox.ini b/tox.ini index fcef147..6bf86c6 100644 --- a/tox.ini +++ b/tox.ini @@ -75,11 +75,11 @@ deps = docutils check-manifest flake8 - collective.checkdocs + readme + pygments usedevelop = true commands = - python setup.py checkdocs - python setup.py check --restructuredtext --strict --metadata + python setup.py check --strict --metadata --restructuredtext check-manifest {toxinidir} flake8 src @@ -112,7 +112,7 @@ setenv = WITH_COVERAGE=yes usedevelop = true commands = - {posargs:py.test --cov src -vv} + {posargs:py.test --cov=src --cov-report=term-missing -vv} deps = {[testenv]deps} pytest-cov @@ -126,7 +126,7 @@ setenv = WITH_COVERAGE=yes usedevelop = true commands = - {posargs:py.test --cov src -vv} + {posargs:py.test --cov=src --cov-report=term-missing -vv} deps = {[testenv]deps} pytest-cov @@ -136,7 +136,6 @@ deps = basepython = python2.6 deps = {[testenv]deps} - pytest-cov trollius [testenv:2.6-nocover-debug] @@ -146,7 +145,6 @@ setenv = ASPECTLIB_DEBUG=yes deps = {[testenv]deps} - pytest-cov trollius [testenv:2.7] @@ -156,7 +154,7 @@ setenv = WITH_COVERAGE=yes usedevelop = true commands = - {posargs:py.test --cov src -vv} + {posargs:py.test --cov=src --cov-report=term-missing -vv} deps = {[testenv]deps} pytest-cov @@ -170,7 +168,7 @@ setenv = WITH_COVERAGE=yes usedevelop = true commands = - {posargs:py.test --cov src -vv} + {posargs:py.test --cov=src --cov-report=term-missing -vv} deps = {[testenv]deps} pytest-cov @@ -180,7 +178,6 @@ deps = basepython = python2.7 deps = {[testenv]deps} - pytest-cov trollius [testenv:2.7-nocover-debug] @@ -190,7 +187,6 @@ setenv = ASPECTLIB_DEBUG=yes deps = {[testenv]deps} - pytest-cov trollius [testenv:3.3] @@ -200,7 +196,7 @@ setenv = WITH_COVERAGE=yes usedevelop = true commands = - {posargs:py.test --cov src -vv} + {posargs:py.test --cov=src --cov-report=term-missing -vv} deps = {[testenv]deps} pytest-cov @@ -214,7 +210,7 @@ setenv = WITH_COVERAGE=yes usedevelop = true commands = - {posargs:py.test --cov src -vv} + {posargs:py.test --cov=src --cov-report=term-missing -vv} deps = {[testenv]deps} pytest-cov @@ -224,7 +220,6 @@ deps = basepython = python3.3 deps = {[testenv]deps} - pytest-cov asyncio [testenv:3.3-nocover-debug] @@ -234,7 +229,6 @@ setenv = ASPECTLIB_DEBUG=yes deps = {[testenv]deps} - pytest-cov asyncio [testenv:3.4] @@ -244,7 +238,7 @@ setenv = WITH_COVERAGE=yes usedevelop = true commands = - {posargs:py.test --cov src -vv} + {posargs:py.test --cov=src --cov-report=term-missing -vv} deps = {[testenv]deps} pytest-cov @@ -257,25 +251,19 @@ setenv = WITH_COVERAGE=yes usedevelop = true commands = - {posargs:py.test --cov src -vv} + {posargs:py.test --cov=src --cov-report=term-missing -vv} deps = {[testenv]deps} pytest-cov [testenv:3.4-nocover] basepython = python3.4 -deps = - {[testenv]deps} - pytest-cov [testenv:3.4-nocover-debug] basepython = python3.4 setenv = {[testenv]setenv} ASPECTLIB_DEBUG=yes -deps = - {[testenv]deps} - pytest-cov [testenv:pypy] basepython = pypy @@ -284,7 +272,7 @@ setenv = WITH_COVERAGE=yes usedevelop = true commands = - {posargs:py.test --cov src -vv} + {posargs:py.test --cov=src --cov-report=term-missing -vv} deps = {[testenv]deps} pytest-cov @@ -298,7 +286,7 @@ setenv = WITH_COVERAGE=yes usedevelop = true commands = - {posargs:py.test --cov src -vv} + {posargs:py.test --cov=src --cov-report=term-missing -vv} deps = {[testenv]deps} pytest-cov @@ -308,7 +296,6 @@ deps = basepython = pypy deps = {[testenv]deps} - pytest-cov trollius [testenv:pypy-nocover-debug] @@ -318,6 +305,5 @@ setenv = ASPECTLIB_DEBUG=yes deps = {[testenv]deps} - pytest-cov trollius From bc832fa25cf74566148f2fb460884d0c2a8429fe Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ionel=20Cristian=20M=C4=83rie=C8=99?= Date: Tue, 24 Feb 2015 02:23:50 +0200 Subject: [PATCH 074/179] Some doc fixes. Add contrib module to reference. --- docs/reference/aspectlib.debug.rst | 4 ++-- docs/reference/aspectlib.rst | 9 +++++---- docs/reference/aspectlib.test.rst | 4 ++-- docs/reference/index.rst | 6 ++++-- docs/requirements.txt | 6 +++--- 5 files changed, 16 insertions(+), 13 deletions(-) diff --git a/docs/reference/aspectlib.debug.rst b/docs/reference/aspectlib.debug.rst index 0b97032..6d9b0ad 100644 --- a/docs/reference/aspectlib.debug.rst +++ b/docs/reference/aspectlib.debug.rst @@ -1,5 +1,5 @@ -aspectlib.debug -=============== +Reference: ``aspectlib.debug`` +============================== .. autosummary:: :nosignatures: diff --git a/docs/reference/aspectlib.rst b/docs/reference/aspectlib.rst index 1ec0847..3a4d002 100644 --- a/docs/reference/aspectlib.rst +++ b/docs/reference/aspectlib.rst @@ -1,5 +1,8 @@ -aspectlib -========= +Reference: ``aspectlib`` +======================== + +Overview +-------- .. highlights:: @@ -24,11 +27,9 @@ aspectlib aspectlib.weave aspectlib.Rollback - Reference --------- - .. automodule:: aspectlib :members: :exclude-members: weave, Aspect diff --git a/docs/reference/aspectlib.test.rst b/docs/reference/aspectlib.test.rst index f1d89c7..7805641 100644 --- a/docs/reference/aspectlib.test.rst +++ b/docs/reference/aspectlib.test.rst @@ -1,5 +1,5 @@ -aspectlib.test -============== +Reference: ``aspectlib.test`` +============================= This module aims to be a lightweight and flexible alternative to the popular `mock `_ framework and more. diff --git a/docs/reference/index.rst b/docs/reference/index.rst index 8426a77..15482c4 100644 --- a/docs/reference/index.rst +++ b/docs/reference/index.rst @@ -2,6 +2,8 @@ Reference ========= .. toctree:: - :glob: - aspectlib* + aspectlib + aspectlib.contrib + aspectlib.debug + aspectlib.test diff --git a/docs/requirements.txt b/docs/requirements.txt index 1632a96..3fa1ada 100644 --- a/docs/requirements.txt +++ b/docs/requirements.txt @@ -1,4 +1,4 @@ -sphinx -sphinxcontrib-napoleon -sphinx-py3doc-enhanced-theme +sphinx==1.2.3 +sphinxcontrib-napoleon==0.3.1 +sphinx-py3doc-enhanced-theme==1.2.0 -e . From bde4d26636cd9ef69a1b0d7b8e0d2ccf2ac587d4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ionel=20Cristian=20M=C4=83rie=C8=99?= Date: Tue, 24 Feb 2015 03:16:00 +0200 Subject: [PATCH 075/179] Add missing file. --- CONTRIBUTING.rst | 89 ++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 89 insertions(+) create mode 100644 CONTRIBUTING.rst diff --git a/CONTRIBUTING.rst b/CONTRIBUTING.rst new file mode 100644 index 0000000..1e58c43 --- /dev/null +++ b/CONTRIBUTING.rst @@ -0,0 +1,89 @@ +============ +Contributing +============ + +Contributions are welcome, and they are greatly appreciated! Every +little bit helps, and credit will always be given. + +Bug reports +=========== + +When `reporting a bug `_ please include: + + * Your operating system name and version. + * Any details about your local setup that might be helpful in troubleshooting. + * Detailed steps to reproduce the bug. + +Documentation improvements +========================== + +aspectlib could always use more documentation, whether as part of the +official aspectlib docs, in docstrings, or even on the web in blog posts, +articles, and such. + +Feature requests and feedback +============================= + +The best way to send feedback is to file an issue at https://github.com/ionelmc/python-aspectlib/issues. + +If you are proposing a feature: + +* Explain in detail how it would work. +* Keep the scope as narrow as possible, to make it easier to implement. +* Remember that this is a volunteer-driven project, and that contributions are welcome :) + +Development +=========== + +To set up `python-aspectlib` for local development: + +1. `Fork python-aspectlib on GitHub `_. +2. Clone your fork locally:: + + git clone git@github.com:your_name_here/python-aspectlib.git + +3. Create a branch for local development:: + + git checkout -b name-of-your-bugfix-or-feature + + Now you can make your changes locally. + +4. When you're done making changes, run all the checks, doc builder and spell checker with `tox `_ one command:: + + tox + +5. Commit your changes and push your branch to GitHub:: + + git add . + git commit -m "Your detailed description of your changes." + git push origin name-of-your-bugfix-or-feature + +6. Submit a pull request through the GitHub website. + +Pull Request Guidelines +----------------------- + +If you need some code review or feedback while you're developing the code just make the pull request. + +For merging, you should: + +1. Include passing tests (run ``tox``) [1]_. +2. Update documentation when there's new API, functionality etc. +3. Add a note to ``CHANGELOG.rst`` about the changes. +4. Add yourself to ``AUTHORS.rst``. + +.. [1] If you don't have all the necessary python versions available locally you can rely on Travis - it will + `run the tests `_ for each change you add in the pull request. + + It will be slower though ... + +Tips +---- + +To run a subset of tests:: + + tox -e envname -- py.test -k test_myfeature + +To run all the test environments in *parallel* (you need to ``pip install detox``):: + + detox \ No newline at end of file From b97e6fd450fdb31b9456bab1626d3e0269b5fabf Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ionel=20Cristian=20M=C4=83rie=C8=99?= Date: Tue, 24 Feb 2015 03:17:39 +0200 Subject: [PATCH 076/179] Improve the retry decorator. Add some tests. --- src/aspectlib/contrib.py | 34 +++++++++++++++++---- tests/test_contrib.py | 64 ++++++++++++++++++++++++++++++++++++++++ 2 files changed, 92 insertions(+), 6 deletions(-) create mode 100644 tests/test_contrib.py diff --git a/src/aspectlib/contrib.py b/src/aspectlib/contrib.py index 073a972..3a7e4b3 100644 --- a/src/aspectlib/contrib.py +++ b/src/aspectlib/contrib.py @@ -13,16 +13,37 @@ def retry(func=None, retries=5, backoff=None, exceptions=(IOError, OSError, EOFE Example:: - >>> + >>> should_fail = lambda foo=[1,2,3]: foo and foo.pop() + >>> @retry + ... def flaky_func(): + ... if should_fail(): + ... raise OSError('Tough luck!') + ... print("Success!") + ... + >>> flaky_func() + Success! + + If it reaches the retry limit:: + + >>> @retry + ... def bad_func(): + ... raise IOError('Tough luck!') + ... + >>> bad_func() + Traceback (most recent call last): + ... + OSError: Tough luck! + """ @Aspect(bind=True) def Retry(cutpoint, *args, **kwargs): for count in range(retries + 1): try: if count and cleanup: - cleanup() + cleanup(*args, **kwargs) yield - except exceptions: + break + except exceptions as exc: if count == retries: raise if not backoff: @@ -31,10 +52,11 @@ def Retry(cutpoint, *args, **kwargs): timeout = backoff else: timeout = backoff(count) - logger.exception("%s(%s, %s) raised exception. %s retries left. Sleeping %s secs.", - cutpoint.__name__, args, kwargs, retries - count, timeout) + logger.exception("%s(%s, %s) raised exception %s. %s retries left. Sleeping %s secs.", + cutpoint.__name__, args, kwargs, exc, retries - count, timeout) sleep(timeout) return Retry if func is None else Retry(func) retry.exponential_backoff = lambda count: 2 ** count -retry.flat_backoff = lambda count: (1, 5)[count] if count < 2 else 15*2**count +retry.straight_backoff = lambda count: (1, 2, 5)[count] if count < 3 else 5 * count - 5 +retry.flat_backoff = lambda count: (1, 2, 5, 10, 15, 30, 60)[count if count < 6 else -1] diff --git a/tests/test_contrib.py b/tests/test_contrib.py new file mode 100644 index 0000000..95e3e06 --- /dev/null +++ b/tests/test_contrib.py @@ -0,0 +1,64 @@ +import time + +import pytest + +from aspectlib.test import Story, record, mock +from aspectlib.contrib import retry +from aspectlib import weave + +def flaky_func(arg): + if arg: + arg.pop() + raise IOError('Tough luck!') + + +def test_done_suceess(): + calls = [] + @retry + def ok_func(): + calls.append(1) + + ok_func() + assert calls == [1] + +def test_defaults(): + calls = [] + retry(sleep=calls.append)(flaky_func)([None] * 5) + assert calls == [0, 0, 0, 0, 0] + + +def test_raises(): + calls = [] + pytest.raises(IOError, retry(sleep=calls.append)(flaky_func), [None] * 6) + assert calls == [0, 0, 0, 0, 0] + + calls = [] + pytest.raises(IOError, retry(sleep=calls.append, retries=1)(flaky_func), [None, None]) + assert calls == [0] + + +def test_backoff(): + calls = [] + retry(sleep=calls.append, backoff=1.5)(flaky_func)([None] * 5) + assert calls == [1.5, 1.5, 1.5, 1.5, 1.5] + + +def test_backoff_exponential(): + calls = [] + retry(sleep=calls.append, retries=10, backoff=retry.exponential_backoff)(flaky_func)([None] * 10) + print(calls) + assert calls == [1, 2, 4, 8, 16, 32, 64, 128, 256, 512] + + +def test_backoff_straight(): + calls = [] + retry(sleep=calls.append, retries=10, backoff=retry.straight_backoff)(flaky_func)([None] * 10) + print(calls) + assert calls == [1, 2, 5, 10, 15, 20, 25, 30, 35, 40] + + +def test_backoff_flat(): + calls = [] + retry(sleep=calls.append, retries=10, backoff=retry.flat_backoff)(flaky_func)([None] * 10) + print(calls) + assert calls == [1, 2, 5, 10, 15, 30, 60, 60, 60, 60] From 7615f70eba0b4b828dcdefa1fcc8cd71d3545a1a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ionel=20Cristian=20M=C4=83rie=C8=99?= Date: Tue, 24 Feb 2015 03:19:21 +0200 Subject: [PATCH 077/179] Remove junk. --- bootstrap.py | 46 ---------------------------------------------- 1 file changed, 46 deletions(-) delete mode 100755 bootstrap.py diff --git a/bootstrap.py b/bootstrap.py deleted file mode 100755 index e8fe968..0000000 --- a/bootstrap.py +++ /dev/null @@ -1,46 +0,0 @@ -#!/usr/bin/env python -import os -import sys -if not os.path.exists('.tox/configure'): - import virtualenv - import subprocess - print("Bootstrapping ...") - virtualenv.create_environment('.tox/configure') - print("Installing `jinja2` and `matrix` into bootstrap environment ...") - if sys.platform == 'win32': - subprocess.check_call(['.tox/configure/Scripts/pip', 'install', 'jinja2', 'matrix']) - else: - subprocess.check_call(['.tox/configure/bin/pip', 'install', 'jinja2', 'matrix']) -if sys.platform == 'win32': - execfile('.tox/configure/Scripts/activate_this.py', dict(__file__='.tox/configure/Scripts/activate_this.py')) -else: - execfile('.tox/configure/bin/activate_this.py', dict(__file__='.tox/configure/bin/activate_this.py')) -import jinja2 -import matrix - -jinja = jinja2.Environment( - loader=jinja2.FileSystemLoader('conf'), - trim_blocks=True, - lstrip_blocks=True, - keep_trailing_newline=True -) -tox_environments = {} -for alias, conf in matrix.from_file('setup.cfg').items(): - python = conf['python_versions'] - deps = conf['dependencies'] - cover = {'false': False, 'true': True}[conf['coverage_flags'].lower()] - env_vars = conf['environment_variables'] - - tox_environments[alias] = { - 'python': 'python' + python if 'py' not in python else python, - 'deps': deps.split(), - 'cover': cover, - 'env_vars': env_vars.split(), - } - -for name in os.listdir('conf'): - with open(name, 'w') as fh: - fh.write(jinja.get_template(name).render(tox_environments=tox_environments)) - print("Wrote %s" % name) - -print("DONE.") \ No newline at end of file From f2451634df4a77780bac4b00cd028d4a2466dd38 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ionel=20Cristian=20M=C4=83rie=C8=99?= Date: Tue, 24 Feb 2015 03:19:31 +0200 Subject: [PATCH 078/179] Change test exception. --- tests/test_contrib.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/tests/test_contrib.py b/tests/test_contrib.py index 95e3e06..2d08463 100644 --- a/tests/test_contrib.py +++ b/tests/test_contrib.py @@ -9,7 +9,7 @@ def flaky_func(arg): if arg: arg.pop() - raise IOError('Tough luck!') + raise OSError('Tough luck!') def test_done_suceess(): @@ -29,11 +29,11 @@ def test_defaults(): def test_raises(): calls = [] - pytest.raises(IOError, retry(sleep=calls.append)(flaky_func), [None] * 6) + pytest.raises(OSError, retry(sleep=calls.append)(flaky_func), [None] * 6) assert calls == [0, 0, 0, 0, 0] calls = [] - pytest.raises(IOError, retry(sleep=calls.append, retries=1)(flaky_func), [None, None]) + pytest.raises(OSError, retry(sleep=calls.append, retries=1)(flaky_func), [None, None]) assert calls == [0] From 6335ea2f524fcd18069b129fe3f2567f68753fce Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ionel=20Cristian=20M=C4=83rie=C8=99?= Date: Tue, 24 Feb 2015 03:21:23 +0200 Subject: [PATCH 079/179] Don't use lambda here --- src/aspectlib/__init__.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/aspectlib/__init__.py b/src/aspectlib/__init__.py index 41cf169..31fcc17 100644 --- a/src/aspectlib/__init__.py +++ b/src/aspectlib/__init__.py @@ -520,7 +520,9 @@ def weave_instance(instance, aspect, methods=NORMAL_METHODS, lazy=False, bag=Bro method_matches = make_method_matcher(methods) logdebug("weave_instance (module=%r, aspect=%s, methods=%s, lazy=%s, **options=%s)", instance, aspect, methods, lazy, options) - fixup = lambda func: func.__get__(instance, type(instance)) + + def fixup(func): + return func.__get__(instance, type(instance)) fixed_aspect = aspect + [fixup] if isinstance(aspect, (list, tuple)) else [aspect, fixup] for attr in dir(instance): From f315706bb5cdc8b8d1ba4b27304f5c0ceaafaaab Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ionel=20Cristian=20M=C4=83rie=C8=99?= Date: Tue, 24 Feb 2015 03:22:06 +0200 Subject: [PATCH 080/179] Use different exception in example. --- src/aspectlib/contrib.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/aspectlib/contrib.py b/src/aspectlib/contrib.py index 3a7e4b3..d8d0ec6 100644 --- a/src/aspectlib/contrib.py +++ b/src/aspectlib/contrib.py @@ -27,7 +27,7 @@ def retry(func=None, retries=5, backoff=None, exceptions=(IOError, OSError, EOFE >>> @retry ... def bad_func(): - ... raise IOError('Tough luck!') + ... raise OSError('Tough luck!') ... >>> bad_func() Traceback (most recent call last): From 4cf041cf2f148bc4290489fd32e63b65c1220f9f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ionel=20Cristian=20M=C4=83rie=C8=99?= Date: Tue, 24 Feb 2015 03:22:33 +0200 Subject: [PATCH 081/179] Change a bit import. --- src/aspectlib/contrib.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/aspectlib/contrib.py b/src/aspectlib/contrib.py index d8d0ec6..3f1e68a 100644 --- a/src/aspectlib/contrib.py +++ b/src/aspectlib/contrib.py @@ -1,12 +1,12 @@ from logging import getLogger -from time import sleep +import time from aspectlib import Aspect logger = getLogger(__name__) -def retry(func=None, retries=5, backoff=None, exceptions=(IOError, OSError, EOFError), cleanup=None, sleep=sleep): +def retry(func=None, retries=5, backoff=None, exceptions=(IOError, OSError, EOFError), cleanup=None, sleep=time.sleep): """ Decorator that retries the call ``retries`` times if ``func`` raises ``exceptions``. Can use a ``backoff`` function to sleep till next retry. From c1dd3dd4730f8839ee33d98306a58dc413e4e806 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ionel=20Cristian=20M=C4=83rie=C8=99?= Date: Tue, 24 Feb 2015 23:46:36 +0200 Subject: [PATCH 082/179] Add ``messages`` property to ``aspectlib.test.LogCapture``. Change ``call`` to have level name instead of number. --- src/aspectlib/test.py | 31 +++++++++++++++++++++---------- 1 file changed, 21 insertions(+), 10 deletions(-) diff --git a/src/aspectlib/test.py b/src/aspectlib/test.py index 74d0a71..661bede 100644 --- a/src/aspectlib/test.py +++ b/src/aspectlib/test.py @@ -4,10 +4,7 @@ from functools import partial from functools import wraps from inspect import isclass -try: - from logging import _levelNames as nameToLevel -except ImportError: - from logging import _nameToLevel as nameToLevel +from logging import getLevelName from logging import getLogger from sys import _getframe from traceback import format_stack @@ -23,6 +20,10 @@ from .utils import repr_ex from .utils import Sentinel +try: + from logging import _levelNames as nameToLevel +except ImportError: + from logging import _nameToLevel as nameToLevel try: from dummy_thread import allocate_lock except ImportError: @@ -98,7 +99,9 @@ class LogCapture(object): >>> logs.assertLogged('Message from error: %s') >>> logs.assertLogged('Message from error: %s') + .. versionchanged:: 1.3.0 + Added ``messages`` property. """ def __init__(self, logger, level='DEBUG'): self._logger = logger @@ -120,24 +123,32 @@ def __exit__(self, *exc): def _callback(self, _binding, _qualname, args, _kwargs): level, message, args = args if level >= self._level: - self._calls.append((message % args if args else message, message, args, level)) + self._calls.append(( + message % args if args else message, + message, + args, + getLevelName(level) + )) @property def calls(self): return [i[1:] for i in self._calls] + @property + def messages(self): + return [(i[-1], i[0]) for i in self._calls] + def has(self, message, *args, **kwargs): level = kwargs.pop('level', None) assert not kwargs, "Unexpected arguments: %s" % kwargs for call_final_message, call_message, call_args, call_level in self._calls: - if ( - (level is None or level == call_level) and ( + if level is None or level == call_level: + if ( message == call_message and args == call_args if args else message == call_final_message or message == call_message - ) - ): - return True + ): + return True return False def assertLogged(self, message, *args, **kwargs): From 08d0f7b66ce84e187b302b61a6834765baf625a2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ionel=20Cristian=20M=C4=83rie=C8=99?= Date: Tue, 24 Feb 2015 23:46:48 +0200 Subject: [PATCH 083/179] Add a test with a class. --- tests/test_contrib.py | 62 +++++++++++++++++++++++++++++++++++++++++-- 1 file changed, 60 insertions(+), 2 deletions(-) diff --git a/tests/test_contrib.py b/tests/test_contrib.py index 2d08463..af6e40a 100644 --- a/tests/test_contrib.py +++ b/tests/test_contrib.py @@ -1,10 +1,11 @@ import time +from logging import getLogger import pytest -from aspectlib.test import Story, record, mock +from aspectlib.test import Story, record, mock, LogCapture from aspectlib.contrib import retry -from aspectlib import weave +from aspectlib import weave, contrib def flaky_func(arg): if arg: @@ -62,3 +63,60 @@ def test_backoff_flat(): retry(sleep=calls.append, retries=10, backoff=retry.flat_backoff)(flaky_func)([None] * 10) print(calls) assert calls == [1, 2, 5, 10, 15, 30, 60, 60, 60, 60] + + +def test_with_class(): + logger = getLogger(__name__) + + class Connection(object): + count = 0 + + @retry + def __init__(self, address): + self.address = address + self.__connect() + + def __connect(self, *_, **__): + self.count += 1 + if self.count % 3: + raise OSError("Failed") + else: + logger.info("connected!") + + @retry(cleanup=__connect) + def action(self, arg1, arg2): + self.count += 1 + if self.count % 3 == 0: + raise OSError("Failed") + else: + logger.info("action!") + + def __repr__(self): + return "Connection@%s" % self.count + + with LogCapture([logger, contrib.logger]) as logcap: + try: + conn = Connection("to-something") + for i in range(5): + conn.action(i, i) + finally: + for i in logcap.messages: + print(i) + assert logcap.messages == [ + ('ERROR', "__init__((Connection@1, 'to-something'), {}) raised exception Failed. 5 retries left. Sleeping 0 secs."), + ('ERROR', "__init__((Connection@2, 'to-something'), {}) raised exception Failed. 4 retries left. Sleeping 0 secs."), + ('INFO', 'connected!'), + ('INFO', 'action!'), + ('INFO', 'action!'), + ('ERROR', 'action((Connection@6, 2, 2), {}) raised exception Failed. 5 retries left. Sleeping 0 secs.'), + ('ERROR', 'action((Connection@7, 2, 2), {}) raised exception Failed. 4 retries left. Sleeping 0 secs.'), + ('ERROR', 'action((Connection@8, 2, 2), {}) raised exception Failed. 3 retries left. Sleeping 0 secs.'), + ('INFO', 'connected!'), + ('INFO', 'action!'), + ('INFO', 'action!'), + ('ERROR', 'action((Connection@12, 4, 4), {}) raised exception Failed. 5 retries left. Sleeping 0 secs.'), + ('ERROR', 'action((Connection@13, 4, 4), {}) raised exception Failed. 4 retries left. Sleeping 0 secs.'), + ('ERROR', 'action((Connection@14, 4, 4), {}) raised exception Failed. 3 retries left. Sleeping 0 secs.'), + ('INFO', 'connected!'), + ('INFO', 'action!'), + ] From 8a4054a389ccd142ae8179b253c5c58fdc75c381 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ionel=20Cristian=20M=C4=83rie=C8=99?= Date: Tue, 24 Feb 2015 23:47:45 +0200 Subject: [PATCH 084/179] Remove some unused imports. --- tests/test_contrib.py | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/tests/test_contrib.py b/tests/test_contrib.py index af6e40a..d5bd9fd 100644 --- a/tests/test_contrib.py +++ b/tests/test_contrib.py @@ -1,11 +1,11 @@ -import time from logging import getLogger import pytest -from aspectlib.test import Story, record, mock, LogCapture +from aspectlib import contrib from aspectlib.contrib import retry -from aspectlib import weave, contrib +from aspectlib.test import LogCapture + def flaky_func(arg): if arg: @@ -22,6 +22,7 @@ def ok_func(): ok_func() assert calls == [1] + def test_defaults(): calls = [] retry(sleep=calls.append)(flaky_func)([None] * 5) From 92abba4369c564d3a9e4529d357713c3adacb3b2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ionel=20Cristian=20M=C4=83rie=C8=99?= Date: Tue, 24 Feb 2015 23:50:56 +0200 Subject: [PATCH 085/179] Fix example. --- src/aspectlib/test.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/aspectlib/test.py b/src/aspectlib/test.py index 661bede..6929484 100644 --- a/src/aspectlib/test.py +++ b/src/aspectlib/test.py @@ -86,7 +86,7 @@ class LogCapture(object): ... logger.info("Message from info: %s", 'somearg') ... logger.error("Message from error: %s", 'somearg') >>> logs.calls - [('Message from info: %s', ('somearg',), 20), ('Message from error: %s', ('somearg',), 40)] + [('Message from info: %s', ('somearg',), 'INFO'), ('Message from error: %s', ('somearg',), 'ERROR')] >>> logs.has('Message from info: %s') True >>> logs.has('Message from info: somearg') From 5b111672aed8eeaa49356dee2f689b30212df100 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ionel=20Cristian=20M=C4=83rie=C8=99?= Date: Tue, 24 Feb 2015 23:51:38 +0200 Subject: [PATCH 086/179] Add example with ``messages``. --- src/aspectlib/test.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/aspectlib/test.py b/src/aspectlib/test.py index 6929484..5d1cc44 100644 --- a/src/aspectlib/test.py +++ b/src/aspectlib/test.py @@ -87,6 +87,8 @@ class LogCapture(object): ... logger.error("Message from error: %s", 'somearg') >>> logs.calls [('Message from info: %s', ('somearg',), 'INFO'), ('Message from error: %s', ('somearg',), 'ERROR')] + >>> logs.messages + [('INFO', 'Message from info: somearg'), ('ERROR', 'Message from error: somearg')] >>> logs.has('Message from info: %s') True >>> logs.has('Message from info: somearg') From 3589239818eed9624daad9d964b35dbaabcca914 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ionel=20Cristian=20M=C4=83rie=C8=99?= Date: Wed, 25 Feb 2015 00:02:52 +0200 Subject: [PATCH 087/179] Update changelog. Add dates. --- docs/changelog.rst | 75 +++++++++++++++++++++++++++------------------- 1 file changed, 45 insertions(+), 30 deletions(-) diff --git a/docs/changelog.rst b/docs/changelog.rst index 7622dfe..bf6b1c3 100644 --- a/docs/changelog.rst +++ b/docs/changelog.rst @@ -1,30 +1,35 @@ Changelog ========= -Version 1.2.2 -------------- +1.3.0 (???) +------------------ + +* Add ``messages`` property to :obj:`aspectlib.test.LogCapture`. Change ``call`` to have level name instead of number. + +1.2.2 (2014-11-25) +------------------ * Add support for weakrefs in the ``__logged__`` wrapper from :obj:`aspectlib.debug.log` decorator. -Version 1.2.1 -------------- +1.2.1 (2014-10-15) +------------------ * Don't raise exceptions from ``Replay.__exit__`` if there would be an error (makes original cause hard to debug). -Version 1.2.0 -------------- +1.2.0 (2014-06-24) +------------------ * Fix weaving methods that were defined in some baseclass (and not on the target class). * Fix wrong qualname beeing used in the Story/Replay recording. Now used the alias given to the weaver instead of whatever is the realname on the current platform. -Version 1.1.1 -------------- +1.1.1 (2014-06-14) +------------------ * Use ``ASPECTLIB_DEBUG`` for every logger in ``aspectlib``. -Version 1.1.0 -------------- +1.1.0 (2014-06-13) +------------------ * Add a `bind` option to :obj:`aspectlib.Aspect` so you can access the cutpoint from the advisor. * Replaced automatic importing in :obj:`aspectlib.test.Replay` with extraction of context variables (locals and globals @@ -35,15 +40,15 @@ Version 1.1.0 * Added :obj:`aspectlib.test.Story.actual` and :obj:`aspectlib.test.Story.expected`. * Added an ``ASPECTLIB_DEBUG`` environment variable option to switch on debug logging in ``aspectlib``'s internals. -Version 1.0.0 -------------- +1.0.0 (2014-05-03) +------------------ * Reworked the internals :obj:`aspectlib.test.Story` to keep call ordering, to allow dependencies and improved the serialization (used in the diffs and the missing/unexpected lists). -Version 0.9.0 -------------- +0.9.0 (2014-04-16) +------------------ * Changed :obj:`aspectlib.test.record`: @@ -59,39 +64,39 @@ Version 0.9.0 * Added :obj:`aspectlib.test.Story` class for capture-replay and stub/mock testing. -Version 0.8.1 -------------- +0.8.1 (2014-04-01) +------------------ * Use simpler import for the py3support. -Version 0.8.0 -------------- +0.8.0 (2014-03-31) +------------------ * Change :obj:`aspectlib.debug.log` to use :obj:`~aspectlib.Aspect` and work as expected with coroutines or generators. * Fixed :obj:`aspectlib.debug.log` to work on Python 3.4. * Remove the undocumented ``aspectlib.Yield`` advice. It was only usable when decorating generators. -Version 0.7.0 -------------- +0.7.0 (2014-03-28) +------------------ * Add support for decorating generators and coroutines in :obj:`~aspectlib.Aspect`. * Made aspectlib raise better exceptions. -Version 0.6.1 -------------- +0.6.1 (2014-03-22) +------------------ * Fix checks inside :obj:`aspectlib.debug.log` that would inadvertently call ``__bool__``/``__nonzero``. -Version 0.6.0 -------------- +0.6.0 (2014-03-17) +------------------ * Don't include __getattribute__ in ALL_METHODS - it's too dangerous dangerous dangerous dangerous dangerous dangerous ... ;) * Do a more reliable check for old-style classes in debug.log * When weaving a class don't weave attributes that are callable but are not actually routines (functions, methods etc) -Version 0.5.0 -------------- +0.5.0 (2014-03-16) +------------------ * Changed :obj:`aspectlib.debug.log`: @@ -106,13 +111,13 @@ Version 0.5.0 (``invalid name`` is not a valid identifier) * Various documentation improvements and examples. -Version 0.4.1 -------------- +0.4.1 (2014-03-08) +------------------ * Remove junk from 0.4.0's source distribution. -Version 0.4.0 -------------- +0.4.0 (2014-03-08) +------------------ * Changed :obj:`aspectlib.weave`: @@ -122,3 +127,13 @@ Version 0.4.0 * Replaced `skip_subclasses` option with `subclasses`. * Fixed weaving methods from a string target. + +0.3.1 (2014-03-05) +------------------ + +* ??? + +0.3.0 (2014-03-05) +------------------ + +* First public release. From 6b5c9ee620d5643da0c34d364f2951a5f48b5641 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ionel=20Cristian=20M=C4=83rie=C8=99?= Date: Wed, 25 Feb 2015 00:03:28 +0200 Subject: [PATCH 088/179] Add note about change. --- src/aspectlib/test.py | 1 + 1 file changed, 1 insertion(+) diff --git a/src/aspectlib/test.py b/src/aspectlib/test.py index 5d1cc44..b1a4606 100644 --- a/src/aspectlib/test.py +++ b/src/aspectlib/test.py @@ -104,6 +104,7 @@ class LogCapture(object): .. versionchanged:: 1.3.0 Added ``messages`` property. + Changed ``calls`` to retrun the level as a string (instead of int). """ def __init__(self, logger, level='DEBUG'): self._logger = logger From 3db07bbd19a2cde8f0800a67f64c6e963fe253f9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ionel=20Cristian=20M=C4=83rie=C8=99?= Date: Sat, 6 Jun 2015 17:45:39 +0300 Subject: [PATCH 089/179] Don't collect .env. --- setup.cfg | 1 + 1 file changed, 1 insertion(+) diff --git a/setup.cfg b/setup.cfg index f312f7a..dc12e37 100644 --- a/setup.cfg +++ b/setup.cfg @@ -12,6 +12,7 @@ exclude = tests/* norecursedirs = .git .tox + .env dist build python_files = From a456700caec580bc0d696eb5beef845dcb9dfcff Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ionel=20Cristian=20M=C4=83rie=C8=99?= Date: Sat, 6 Jun 2015 17:46:42 +0300 Subject: [PATCH 090/179] Fixed a bogus warning from `patch_module` when patching methods on old style classes. --- docs/changelog.rst | 13 +++++++------ src/aspectlib/__init__.py | 1 + tests/test_aspectlib.py | 9 +++++++++ 3 files changed, 17 insertions(+), 6 deletions(-) diff --git a/docs/changelog.rst b/docs/changelog.rst index bf6b1c3..3cf875d 100644 --- a/docs/changelog.rst +++ b/docs/changelog.rst @@ -1,15 +1,16 @@ Changelog ========= -1.3.0 (???) +1.3.0 (2015-06-06) ------------------ -* Add ``messages`` property to :obj:`aspectlib.test.LogCapture`. Change ``call`` to have level name instead of number. +* Added ``messages`` property to :obj:`aspectlib.test.LogCapture`. Change ``call`` to have level name instead of number. +* Fixed a bogus warning from :func:`aspectlib.patch_module`` when patching methods on old style classes. 1.2.2 (2014-11-25) ------------------ -* Add support for weakrefs in the ``__logged__`` wrapper from :obj:`aspectlib.debug.log` decorator. +* Added support for weakrefs in the ``__logged__`` wrapper from :obj:`aspectlib.debug.log` decorator. 1.2.1 (2014-10-15) ------------------ @@ -19,8 +20,8 @@ Changelog 1.2.0 (2014-06-24) ------------------ -* Fix weaving methods that were defined in some baseclass (and not on the target class). -* Fix wrong qualname beeing used in the Story/Replay recording. Now used the alias given to the weaver instead of +* Fixed weaving methods that were defined in some baseclass (and not on the target class). +* Fixed wrong qualname beeing used in the Story/Replay recording. Now used the alias given to the weaver instead of whatever is the realname on the current platform. 1.1.1 (2014-06-14) @@ -31,7 +32,7 @@ Changelog 1.1.0 (2014-06-13) ------------------ -* Add a `bind` option to :obj:`aspectlib.Aspect` so you can access the cutpoint from the advisor. +* Added a `bind` option to :obj:`aspectlib.Aspect` so you can access the cutpoint from the advisor. * Replaced automatic importing in :obj:`aspectlib.test.Replay` with extraction of context variables (locals and globals from the calling :obj:`aspectlib.test.Story`). Works better than the previous inference of module from AST of the result. diff --git a/src/aspectlib/__init__.py b/src/aspectlib/__init__.py index 31fcc17..5dfb09e 100644 --- a/src/aspectlib/__init__.py +++ b/src/aspectlib/__init__.py @@ -699,6 +699,7 @@ def patch_module(module, name, replacement, original=UNSPECIFIED, aliases=True, logdebug("= saving %s on %s.%s ...", replacement, target, alias) setattr(module, alias, replacement) rollback.merge(lambda alias=alias: setattr(module, alias, original)) + seen = True else: raise AssertionError("%s.%s = %s is not %s." % (module, alias, obj, original)) diff --git a/tests/test_aspectlib.py b/tests/test_aspectlib.py index 2ab277d..62a986e 100644 --- a/tests/test_aspectlib.py +++ b/tests/test_aspectlib.py @@ -417,6 +417,15 @@ def test_weave_str_class_meth_target(): assert Stuff().meth() is None +def test_weave_old_style_method_no_warn_patch_module(): + calls = [] + with aspectlib.weave('warnings.warn', record(calls=calls)): + with aspectlib.weave('test_aspectlib.LegacyTestClass.foobar', mock('stuff')): + assert LegacyTestClass().foobar() == 'stuff' + + assert calls == [] + + def test_weave_wrong_module(): calls = [] with aspectlib.weave('warnings.warn', record(calls=calls)): From 9693a5df9a992870e72e93c8dbfdcc80e1ed0961 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ionel=20Cristian=20M=C4=83rie=C8=99?= Date: Sat, 6 Jun 2015 17:51:34 +0300 Subject: [PATCH 091/179] Add to gitignore too. --- .gitignore | 1 + 1 file changed, 1 insertion(+) diff --git a/.gitignore b/.gitignore index fc6d847..0b72c33 100644 --- a/.gitignore +++ b/.gitignore @@ -50,5 +50,6 @@ docs/_build .*.sw[po] .build .ve +.env .bootstrap *.bak From eef20072107634d2cbbd52ba8176057f9218e26f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ionel=20Cristian=20M=C4=83rie=C8=99?= Date: Sat, 6 Jun 2015 19:56:43 +0300 Subject: [PATCH 092/179] Strip func text role too. --- setup.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/setup.py b/setup.py index 9d50363..b961354 100644 --- a/setup.py +++ b/setup.py @@ -28,7 +28,7 @@ def read(*names, **kwargs): description="aspectlib is an aspect-oriented programming, monkey-patch and decorators library. It is useful when " "changing behavior in existing code is desired. It includes tools for debugging and testing: simple " "mock/record and a complete capture/replay framework.", - long_description="%s\n%s" % (read('README.rst'), re.sub(':obj:`~?(.*?)`', r'``\1``', read('docs', 'changelog.rst'))), + long_description="%s\n%s" % (read('README.rst'), re.sub(':(obj|func):`~?(.*?)`', r'``\1``', read('docs', 'changelog.rst'))), author='Ionel Cristian Mărieș', author_email='contact@ionelmc.ro', url='https://github.com/ionelmc/python-aspectlib', From 443e5a80b9ffda2702d260202d54fb0898a0a94b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ionel=20Cristian=20M=C4=83rie=C8=99?= Date: Sat, 6 Jun 2015 19:56:58 +0300 Subject: [PATCH 093/179] =?UTF-8?q?Bump=20version:=201.2.2=20=E2=86=92=201?= =?UTF-8?q?.3.0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .bumpversion.cfg | 2 +- docs/conf.py | 2 +- setup.py | 2 +- src/aspectlib/__init__.py | 2 +- 4 files changed, 4 insertions(+), 4 deletions(-) diff --git a/.bumpversion.cfg b/.bumpversion.cfg index 680c886..7a406ae 100644 --- a/.bumpversion.cfg +++ b/.bumpversion.cfg @@ -1,5 +1,5 @@ [bumpversion] -current_version = 1.2.2 +current_version = 1.3.0 files = setup.py docs/conf.py src/aspectlib/__init__.py commit = True tag = False diff --git a/docs/conf.py b/docs/conf.py index a47e9fd..aa883db 100644 --- a/docs/conf.py +++ b/docs/conf.py @@ -24,7 +24,7 @@ year = u'2015' author = u'Ionel Cristian Maries' copyright = '{0}, {1}'.format(year, author) -version = release = u'1.2.2' +version = release = u'1.3.0' import sphinx_py3doc_enhanced_theme html_theme = "sphinx_py3doc_enhanced_theme" diff --git a/setup.py b/setup.py index b961354..7407aa9 100644 --- a/setup.py +++ b/setup.py @@ -23,7 +23,7 @@ def read(*names, **kwargs): setup( name='aspectlib', - version='1.2.2', + version='1.3.0', license='BSD', description="aspectlib is an aspect-oriented programming, monkey-patch and decorators library. It is useful when " "changing behavior in existing code is desired. It includes tools for debugging and testing: simple " diff --git a/src/aspectlib/__init__.py b/src/aspectlib/__init__.py index 5dfb09e..4d5715f 100644 --- a/src/aspectlib/__init__.py +++ b/src/aspectlib/__init__.py @@ -40,7 +40,7 @@ __all__ = 'weave', 'Aspect', 'Proceed', 'Return', 'ALL_METHODS', 'NORMAL_METHODS' -__version__ = '1.2.2' +__version__ = '1.3.0' logger = getLogger(__name__) logdebug = logf(logger.debug) From 7e2c541f87efc20986ef1e0f8d8f38d707b363cd Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ionel=20Cristian=20M=C4=83rie=C8=99?= Date: Wed, 10 Jun 2015 05:11:39 +0300 Subject: [PATCH 094/179] Remove this pycharm stuff. --- .idea/.name | 1 - .idea/codeStyleSettings.xml | 23 - .idea/encodings.xml | 5 - .idea/misc.xml | 5 - .idea/modules.xml | 9 - .idea/python-aspectlib.iml | 12 - .idea/scopes/scope_settings.xml | 5 - .idea/vagrant.xml | 9 - .idea/vcs.xml | 7 - .idea/workspace.xml | 743 -------------------------------- 10 files changed, 819 deletions(-) delete mode 100644 .idea/.name delete mode 100644 .idea/codeStyleSettings.xml delete mode 100644 .idea/encodings.xml delete mode 100644 .idea/misc.xml delete mode 100644 .idea/modules.xml delete mode 100644 .idea/python-aspectlib.iml delete mode 100644 .idea/scopes/scope_settings.xml delete mode 100644 .idea/vagrant.xml delete mode 100644 .idea/vcs.xml delete mode 100644 .idea/workspace.xml diff --git a/.idea/.name b/.idea/.name deleted file mode 100644 index bae07c9..0000000 --- a/.idea/.name +++ /dev/null @@ -1 +0,0 @@ -python-aspectlib \ No newline at end of file diff --git a/.idea/codeStyleSettings.xml b/.idea/codeStyleSettings.xml deleted file mode 100644 index 1d86f61..0000000 --- a/.idea/codeStyleSettings.xml +++ /dev/null @@ -1,23 +0,0 @@ - - - - - - - diff --git a/.idea/encodings.xml b/.idea/encodings.xml deleted file mode 100644 index e206d70..0000000 --- a/.idea/encodings.xml +++ /dev/null @@ -1,5 +0,0 @@ - - - - - diff --git a/.idea/misc.xml b/.idea/misc.xml deleted file mode 100644 index 0cf99cc..0000000 --- a/.idea/misc.xml +++ /dev/null @@ -1,5 +0,0 @@ - - - - - diff --git a/.idea/modules.xml b/.idea/modules.xml deleted file mode 100644 index c476cef..0000000 --- a/.idea/modules.xml +++ /dev/null @@ -1,9 +0,0 @@ - - - - - - - - - diff --git a/.idea/python-aspectlib.iml b/.idea/python-aspectlib.iml deleted file mode 100644 index 3f41913..0000000 --- a/.idea/python-aspectlib.iml +++ /dev/null @@ -1,12 +0,0 @@ - - - - - - - - - - - - diff --git a/.idea/scopes/scope_settings.xml b/.idea/scopes/scope_settings.xml deleted file mode 100644 index 922003b..0000000 --- a/.idea/scopes/scope_settings.xml +++ /dev/null @@ -1,5 +0,0 @@ - - - - \ No newline at end of file diff --git a/.idea/vagrant.xml b/.idea/vagrant.xml deleted file mode 100644 index 9b58cdc..0000000 --- a/.idea/vagrant.xml +++ /dev/null @@ -1,9 +0,0 @@ - - - - - - diff --git a/.idea/vcs.xml b/.idea/vcs.xml deleted file mode 100644 index c80f219..0000000 --- a/.idea/vcs.xml +++ /dev/null @@ -1,7 +0,0 @@ - - - - - - - diff --git a/.idea/workspace.xml b/.idea/workspace.xml deleted file mode 100644 index 1664e94..0000000 --- a/.idea/workspace.xml +++ /dev/null @@ -1,743 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - CSS - - - Data flow issuesJavaScript - - - Invalid elementsCSS - - - JavaScript - - - Naming conventionsJavaScript - - - Probable bugsJavaScript - - - Python - - - - - Buildout - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - 1397306506744 - 1397306506744 - - - 1397413457199 - 1397413457200 - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - From 18e45858a40d656a0292b83f82bd1fe15b641117 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ionel=20Cristian=20M=C4=83rie=C8=99?= Date: Wed, 10 Jun 2015 05:19:41 +0300 Subject: [PATCH 095/179] Add missing doc for aspectlib.contrib. --- docs/reference/aspectlib.contrib.rst | 13 +++++++++++++ src/aspectlib/contrib.py | 23 ++++++++++++++++++++--- 2 files changed, 33 insertions(+), 3 deletions(-) create mode 100644 docs/reference/aspectlib.contrib.rst diff --git a/docs/reference/aspectlib.contrib.rst b/docs/reference/aspectlib.contrib.rst new file mode 100644 index 0000000..34b73cb --- /dev/null +++ b/docs/reference/aspectlib.contrib.rst @@ -0,0 +1,13 @@ +Reference: ``aspectlib.debug`` +============================== + +.. autosummary:: + :nosignatures: + + aspectlib.contrib.retry + aspectlib.contrib.retry.exponential_backoff + aspectlib.contrib.retry.straight_backoff + aspectlib.contrib.retry.flat_backoff + +.. automodule:: aspectlib.contrib + :members: diff --git a/src/aspectlib/contrib.py b/src/aspectlib/contrib.py index 3f1e68a..465829e 100644 --- a/src/aspectlib/contrib.py +++ b/src/aspectlib/contrib.py @@ -57,6 +57,23 @@ def Retry(cutpoint, *args, **kwargs): sleep(timeout) return Retry if func is None else Retry(func) -retry.exponential_backoff = lambda count: 2 ** count -retry.straight_backoff = lambda count: (1, 2, 5)[count] if count < 3 else 5 * count - 5 -retry.flat_backoff = lambda count: (1, 2, 5, 10, 15, 30, 60)[count if count < 6 else -1] +def exponential_backoff(count): + """ + Wait 2**N seconds. + """ + return 2 ** count +retry.exponential_backoff = exponential_backoff + +def straight_backoff(count): + """ + Wait 1, 2, 5 seconds. All retries after the 3rd retry will wait 5*N-5 seconds. + """ + return (1, 2, 5)[count] if count < 3 else 5 * count - 5 +retry.straight_backoff = straight_backoff + +def flat_backoff(count): + """ + Wait 1, 2, 5, 10, 15, 30 and 60 seconds. All retries after the 5th retry will wait 60 seconds. + """ + return (1, 2, 5, 10, 15, 30, 60)[count if count < 6 else -1] +retry.flat_backoff = flat_backoff From 145065825b767454b60a2a9f3c7289ccd2f34293 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ionel=20Cristian=20M=C4=83rie=C8=99?= Date: Wed, 10 Jun 2015 10:47:51 +0300 Subject: [PATCH 096/179] A bit of styling. --- src/aspectlib/contrib.py | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/src/aspectlib/contrib.py b/src/aspectlib/contrib.py index 465829e..2190550 100644 --- a/src/aspectlib/contrib.py +++ b/src/aspectlib/contrib.py @@ -35,8 +35,9 @@ def retry(func=None, retries=5, backoff=None, exceptions=(IOError, OSError, EOFE OSError: Tough luck! """ + @Aspect(bind=True) - def Retry(cutpoint, *args, **kwargs): + def retry_aspect(cutpoint, *args, **kwargs): for count in range(retries + 1): try: if count and cleanup: @@ -55,7 +56,9 @@ def Retry(cutpoint, *args, **kwargs): logger.exception("%s(%s, %s) raised exception %s. %s retries left. Sleeping %s secs.", cutpoint.__name__, args, kwargs, exc, retries - count, timeout) sleep(timeout) - return Retry if func is None else Retry(func) + + return retry_aspect if func is None else retry_aspect(func) + def exponential_backoff(count): """ @@ -64,6 +67,7 @@ def exponential_backoff(count): return 2 ** count retry.exponential_backoff = exponential_backoff + def straight_backoff(count): """ Wait 1, 2, 5 seconds. All retries after the 3rd retry will wait 5*N-5 seconds. @@ -71,6 +75,7 @@ def straight_backoff(count): return (1, 2, 5)[count] if count < 3 else 5 * count - 5 retry.straight_backoff = straight_backoff + def flat_backoff(count): """ Wait 1, 2, 5, 10, 15, 30 and 60 seconds. All retries after the 5th retry will wait 60 seconds. From 8b7411794561cd5059bcf38deccf482d604cc551 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ionel=20Cristian=20M=C4=83rie=C8=99?= Date: Fri, 21 Aug 2015 14:59:18 +0300 Subject: [PATCH 097/179] Use newer theme. --- docs/requirements.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/requirements.txt b/docs/requirements.txt index 3fa1ada..ee4fdfe 100644 --- a/docs/requirements.txt +++ b/docs/requirements.txt @@ -1,4 +1,4 @@ sphinx==1.2.3 sphinxcontrib-napoleon==0.3.1 -sphinx-py3doc-enhanced-theme==1.2.0 +sphinx-py3doc-enhanced-theme==2.2.0 -e . From 001275e0895fac70da715d82fdf5252ce2b48331 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ionel=20Cristian=20M=C4=83rie=C8=99?= Date: Fri, 21 Aug 2015 15:03:43 +0300 Subject: [PATCH 098/179] Upgrade some more deps for docs. --- docs/requirements.txt | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/docs/requirements.txt b/docs/requirements.txt index ee4fdfe..b26d50e 100644 --- a/docs/requirements.txt +++ b/docs/requirements.txt @@ -1,4 +1,4 @@ -sphinx==1.2.3 -sphinxcontrib-napoleon==0.3.1 +sphinx==1.3.1 +sphinxcontrib-napoleon==0.3.11 sphinx-py3doc-enhanced-theme==2.2.0 -e . From b2146958167e85c5188e75a982629cbe3baaa436 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ionel=20Cristian=20M=C4=83rie=C8=99?= Date: Fri, 21 Aug 2015 16:16:01 +0300 Subject: [PATCH 099/179] Update requirements.txt --- docs/requirements.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/requirements.txt b/docs/requirements.txt index b26d50e..7227a52 100644 --- a/docs/requirements.txt +++ b/docs/requirements.txt @@ -1,4 +1,4 @@ sphinx==1.3.1 sphinxcontrib-napoleon==0.3.11 -sphinx-py3doc-enhanced-theme==2.2.0 +sphinx-py3doc-enhanced-theme==2.2.1 -e . From 16c12fbfd31e01841fb3ac4f7dc044ff15eac487 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ionel=20Cristian=20M=C4=83rie=C8=99?= Date: Sat, 12 Sep 2015 02:06:48 +0300 Subject: [PATCH 100/179] Update skel. --- .bumpversion.cfg | 6 +- .cookiecutterrc | 34 ++++++++++ .gitignore | 9 ++- .travis.yml | 30 +++++---- AUTHORS.rst | 5 ++ CHANGELOG.rst | 8 +++ CONTRIBUTING.rst | 4 +- LICENSE | 38 +++++------ appveyor.yml | 131 +++++++++++++++++++++++++++++++++++--- ci/appveyor-bootstrap.ps1 | 3 +- ci/bootstrap.py | 24 ++++--- ci/templates/.travis.yml | 12 +++- ci/templates/appveyor.yml | 21 ++++-- ci/templates/tox.ini | 62 ++++++++++++------ docs/authors.rst | 1 + docs/conf.py | 15 ++--- docs/requirements.txt | 6 +- setup.cfg | 38 +++++++++-- setup.py | 2 +- tox.ini | 118 ++++++++++++++++++++-------------- 20 files changed, 415 insertions(+), 152 deletions(-) create mode 100644 .cookiecutterrc create mode 100644 AUTHORS.rst create mode 100644 CHANGELOG.rst create mode 100644 docs/authors.rst diff --git a/.bumpversion.cfg b/.bumpversion.cfg index 7a406ae..57515e8 100644 --- a/.bumpversion.cfg +++ b/.bumpversion.cfg @@ -1,6 +1,8 @@ [bumpversion] current_version = 1.3.0 -files = setup.py docs/conf.py src/aspectlib/__init__.py commit = True -tag = False +tag = True +[bumpversion:file:setup.py] +[bumpversion:file:docs/conf.py] +[bumpversion:file:src/aspectlib/__init__.py] diff --git a/.cookiecutterrc b/.cookiecutterrc new file mode 100644 index 0000000..e1a90d3 --- /dev/null +++ b/.cookiecutterrc @@ -0,0 +1,34 @@ +# This file exists so you can easily regenerate your project. +# +# Unfortunatelly cookiecutter can't use this right away so +# you have to copy this file to ~/.cookiecutterrc + +default_context: + + appveyor: 'yes' + c_extension_optional: 'no' + c_extension_support: 'no' + codacy: 'yes' + codeclimate: 'yes' + codecov: 'yes' + command_line_interface: 'no' + coveralls: 'yes' + distribution_name: 'aspectlib' + email: 'contact@ionelmc.ro' + full_name: 'Ionel Cristian Mărieș' + github_username: 'ionelmc' + landscape: 'yes' + package_name: 'aspectlib' + project_name: 'Aspectlib' + project_short_description: '``aspectlib`` is an aspect-oriented programming, monkey-patch and decorators library. It is useful when changing' + release_date: '2015-06-06' + repo_name: 'python-aspectlib' + requiresio: 'yes' + scrutinizer: 'yes' + sphinx_theme: 'sphinx-py3doc-enhanced-theme' + test_matrix_configurator: 'yes' + test_runner: 'pytest' + travis: 'yes' + version: '1.3.0' + website: 'http://blog.ionelmc.ro' + year: '2014-2015' diff --git a/.gitignore b/.gitignore index 0b72c33..cae27ab 100644 --- a/.gitignore +++ b/.gitignore @@ -9,6 +9,7 @@ dist build eggs +.eggs parts bin var @@ -17,6 +18,8 @@ develop-eggs .installed.cfg lib lib64 +venv*/ +pyvenv*/ # Installer logs pip-log.txt @@ -24,9 +27,9 @@ pip-log.txt # Unit test / coverage reports .coverage .tox -.coverage .coverage.* nosetests.xml +coverage.xml htmlcov # Translations @@ -37,6 +40,8 @@ htmlcov .project .pydevproject .idea +*.iml +*.komodoproject # Complexity output/*.html @@ -51,5 +56,7 @@ docs/_build .build .ve .env +.cache +.pytest .bootstrap *.bak diff --git a/.travis.yml b/.travis.yml index aac1956..6ec991d 100644 --- a/.travis.yml +++ b/.travis.yml @@ -6,37 +6,43 @@ env: LD_PRELOAD=/lib/x86_64-linux-gnu/libSegFault.so matrix: - TOXENV=check - - TOXENV=2.6,coveralls - - TOXENV=2.6-debug,coveralls + - TOXENV=2.6,coveralls,codecov + - TOXENV=2.6-debug,coveralls,codecov - TOXENV=2.6-nocover - TOXENV=2.6-nocover-debug - - TOXENV=2.7,coveralls - - TOXENV=2.7-debug,coveralls + - TOXENV=2.7,coveralls,codecov + - TOXENV=2.7-debug,coveralls,codecov - TOXENV=2.7-nocover - TOXENV=2.7-nocover-debug - - TOXENV=3.3,coveralls - - TOXENV=3.3-debug,coveralls + - TOXENV=3.3,coveralls,codecov + - TOXENV=3.3-debug,coveralls,codecov - TOXENV=3.3-nocover - TOXENV=3.3-nocover-debug - - TOXENV=3.4,coveralls - - TOXENV=3.4-debug,coveralls + - TOXENV=3.4,coveralls,codecov + - TOXENV=3.4-debug,coveralls,codecov - TOXENV=3.4-nocover - TOXENV=3.4-nocover-debug - - TOXENV=pypy,coveralls - - TOXENV=pypy-debug,coveralls + - TOXENV=pypy,coveralls,codecov + - TOXENV=pypy-debug,coveralls,codecov - TOXENV=pypy-nocover - TOXENV=pypy-nocover-debug before_install: - python --version - - virtualenv --version - - pip --version - uname -a - lsb_release -a install: - pip install tox + - virtualenv --version + - easy_install --version + - pip --version + - tox --version script: - tox -v +after_failure: + - more .tox/log/* | cat + - more .tox/*/log/* | cat notifications: email: on_success: never on_failure: always + diff --git a/AUTHORS.rst b/AUTHORS.rst new file mode 100644 index 0000000..0e54887 --- /dev/null +++ b/AUTHORS.rst @@ -0,0 +1,5 @@ + +Authors +======= + +* Ionel Cristian Mărieș - http://blog.ionelmc.ro diff --git a/CHANGELOG.rst b/CHANGELOG.rst new file mode 100644 index 0000000..d7d7d48 --- /dev/null +++ b/CHANGELOG.rst @@ -0,0 +1,8 @@ + +Changelog +========= + +1.3.0 (2015-06-06) +----------------------------------------- + +* First release on PyPI. diff --git a/CONTRIBUTING.rst b/CONTRIBUTING.rst index 1e58c43..723d2af 100644 --- a/CONTRIBUTING.rst +++ b/CONTRIBUTING.rst @@ -17,8 +17,8 @@ When `reporting a bug `_ ple Documentation improvements ========================== -aspectlib could always use more documentation, whether as part of the -official aspectlib docs, in docstrings, or even on the web in blog posts, +Aspectlib could always use more documentation, whether as part of the +official Aspectlib docs, in docstrings, or even on the web in blog posts, articles, and such. Feature requests and feedback diff --git a/LICENSE b/LICENSE index 484c287..0209986 100644 --- a/LICENSE +++ b/LICENSE @@ -1,19 +1,19 @@ -Copyright (c) 2014, Ionel Cristian Mărieș -All rights reserved. - -Redistribution and use in source and binary forms, with or without modification, are permitted provided that the -following conditions are met: - -1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following -disclaimer. - -2. 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. - -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. +Copyright (c) 2014-2015, Ionel Cristian Mărieș +All rights reserved. + +Redistribution and use in source and binary forms, with or without modification, are permitted provided that the +following conditions are met: + +1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following +disclaimer. + +2. 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. + +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. diff --git a/appveyor.yml b/appveyor.yml index 424e226..d2a893c 100644 --- a/appveyor.yml +++ b/appveyor.yml @@ -1,47 +1,157 @@ -version: '{build}' +version: '{branch}-{build}' build: off environment: + global: + WITH_COMPILER: "cmd /E:ON /V:ON /C .\\ci\\appveyor-with-compiler.cmd" matrix: - TOXENV: check PYTHON_HOME: "C:\\Python27" PYTHON_VERSION: "2.7" PYTHON_ARCH: "32" - - TOXENV: "2.7" + - TOXENV: "2.7,codecov" TOXPYTHON: "C:\\Python27\\python.exe" WINDOWS_SDK_VERSION: "v7.0" PYTHON_HOME: "C:\\Python27" PYTHON_VERSION: "2.7" PYTHON_ARCH: "32" - - TOXENV: "2.7-debug" + - TOXENV: "2.7,codecov" + TOXPYTHON: "C:\\Python27-x64\\python.exe" + WINDOWS_SDK_VERSION: "v7.0" + PYTHON_HOME: "C:\\Python27-x64" + PYTHON_VERSION: "2.7" + PYTHON_ARCH: "64" + - TOXENV: "2.7-debug,codecov" + TOXPYTHON: "C:\\Python27\\python.exe" + WINDOWS_SDK_VERSION: "v7.0" + PYTHON_HOME: "C:\\Python27" + PYTHON_VERSION: "2.7" + PYTHON_ARCH: "32" + - TOXENV: "2.7-debug,codecov" + TOXPYTHON: "C:\\Python27-x64\\python.exe" + WINDOWS_SDK_VERSION: "v7.0" + PYTHON_HOME: "C:\\Python27-x64" + PYTHON_VERSION: "2.7" + PYTHON_ARCH: "64" + - TOXENV: "2.7-nocover" TOXPYTHON: "C:\\Python27\\python.exe" WINDOWS_SDK_VERSION: "v7.0" PYTHON_HOME: "C:\\Python27" PYTHON_VERSION: "2.7" PYTHON_ARCH: "32" - - TOXENV: "3.3" + - TOXENV: "2.7-nocover" + TOXPYTHON: "C:\\Python27-x64\\python.exe" + WINDOWS_SDK_VERSION: "v7.0" + PYTHON_HOME: "C:\\Python27-x64" + PYTHON_VERSION: "2.7" + PYTHON_ARCH: "64" + - TOXENV: "2.7-nocover-debug,codecov" + TOXPYTHON: "C:\\Python27\\python.exe" + WINDOWS_SDK_VERSION: "v7.0" + PYTHON_HOME: "C:\\Python27" + PYTHON_VERSION: "2.7" + PYTHON_ARCH: "32" + - TOXENV: "2.7-nocover-debug,codecov" + TOXPYTHON: "C:\\Python27-x64\\python.exe" + WINDOWS_SDK_VERSION: "v7.0" + PYTHON_HOME: "C:\\Python27-x64" + PYTHON_VERSION: "2.7" + PYTHON_ARCH: "64" + - TOXENV: "3.3,codecov" + TOXPYTHON: "C:\\Python33\\python.exe" + WINDOWS_SDK_VERSION: "v7.1" + PYTHON_HOME: "C:\\Python33" + PYTHON_VERSION: "3.3" + PYTHON_ARCH: "32" + - TOXENV: "3.3,codecov" + TOXPYTHON: "C:\\Python33-x64\\python.exe" + WINDOWS_SDK_VERSION: "v7.1" + PYTHON_HOME: "C:\\Python33-x64" + PYTHON_VERSION: "3.3" + PYTHON_ARCH: "64" + - TOXENV: "3.3-debug,codecov" + TOXPYTHON: "C:\\Python33\\python.exe" + WINDOWS_SDK_VERSION: "v7.1" + PYTHON_HOME: "C:\\Python33" + PYTHON_VERSION: "3.3" + PYTHON_ARCH: "32" + - TOXENV: "3.3-debug,codecov" + TOXPYTHON: "C:\\Python33-x64\\python.exe" + WINDOWS_SDK_VERSION: "v7.1" + PYTHON_HOME: "C:\\Python33-x64" + PYTHON_VERSION: "3.3" + PYTHON_ARCH: "64" + - TOXENV: "3.3-nocover" TOXPYTHON: "C:\\Python33\\python.exe" WINDOWS_SDK_VERSION: "v7.1" PYTHON_HOME: "C:\\Python33" PYTHON_VERSION: "3.3" PYTHON_ARCH: "32" - - TOXENV: "3.3-debug" + - TOXENV: "3.3-nocover" + TOXPYTHON: "C:\\Python33-x64\\python.exe" + WINDOWS_SDK_VERSION: "v7.1" + PYTHON_HOME: "C:\\Python33-x64" + PYTHON_VERSION: "3.3" + PYTHON_ARCH: "64" + - TOXENV: "3.3-nocover-debug,codecov" TOXPYTHON: "C:\\Python33\\python.exe" WINDOWS_SDK_VERSION: "v7.1" PYTHON_HOME: "C:\\Python33" PYTHON_VERSION: "3.3" PYTHON_ARCH: "32" - - TOXENV: "3.4" + - TOXENV: "3.3-nocover-debug,codecov" + TOXPYTHON: "C:\\Python33-x64\\python.exe" + WINDOWS_SDK_VERSION: "v7.1" + PYTHON_HOME: "C:\\Python33-x64" + PYTHON_VERSION: "3.3" + PYTHON_ARCH: "64" + - TOXENV: "3.4,codecov" TOXPYTHON: "C:\\Python34\\python.exe" WINDOWS_SDK_VERSION: "v7.1" PYTHON_HOME: "C:\\Python34" PYTHON_VERSION: "3.4" PYTHON_ARCH: "32" - - TOXENV: "3.4-debug" + - TOXENV: "3.4,codecov" + TOXPYTHON: "C:\\Python34-x64\\python.exe" + WINDOWS_SDK_VERSION: "v7.1" + PYTHON_HOME: "C:\\Python34-x64" + PYTHON_VERSION: "3.4" + PYTHON_ARCH: "64" + - TOXENV: "3.4-debug,codecov" TOXPYTHON: "C:\\Python34\\python.exe" WINDOWS_SDK_VERSION: "v7.1" PYTHON_HOME: "C:\\Python34" PYTHON_VERSION: "3.4" PYTHON_ARCH: "32" + - TOXENV: "3.4-debug,codecov" + TOXPYTHON: "C:\\Python34-x64\\python.exe" + WINDOWS_SDK_VERSION: "v7.1" + PYTHON_HOME: "C:\\Python34-x64" + PYTHON_VERSION: "3.4" + PYTHON_ARCH: "64" + - TOXENV: "3.4-nocover" + TOXPYTHON: "C:\\Python34\\python.exe" + WINDOWS_SDK_VERSION: "v7.1" + PYTHON_HOME: "C:\\Python34" + PYTHON_VERSION: "3.4" + PYTHON_ARCH: "32" + - TOXENV: "3.4-nocover" + TOXPYTHON: "C:\\Python34-x64\\python.exe" + WINDOWS_SDK_VERSION: "v7.1" + PYTHON_HOME: "C:\\Python34-x64" + PYTHON_VERSION: "3.4" + PYTHON_ARCH: "64" + - TOXENV: "3.4-nocover-debug,codecov" + TOXPYTHON: "C:\\Python34\\python.exe" + WINDOWS_SDK_VERSION: "v7.1" + PYTHON_HOME: "C:\\Python34" + PYTHON_VERSION: "3.4" + PYTHON_ARCH: "32" + - TOXENV: "3.4-nocover-debug,codecov" + TOXPYTHON: "C:\\Python34-x64\\python.exe" + WINDOWS_SDK_VERSION: "v7.1" + PYTHON_HOME: "C:\\Python34-x64" + PYTHON_VERSION: "3.4" + PYTHON_ARCH: "64" init: - "ECHO %TOXENV%" - ps: "ls C:\\Python*" @@ -51,4 +161,9 @@ test_script: - "%PYTHON_HOME%\\Scripts\\tox --version" - "%PYTHON_HOME%\\Scripts\\virtualenv --version" - "%PYTHON_HOME%\\Scripts\\pip --version" - - "%PYTHON_HOME%\\Scripts\\tox" + - "%WITH_COMPILER% %PYTHON_HOME%\\Scripts\\tox" +after_test: + - "IF \"%TOXENV:~-8,8%\" == \"-nocover\" %WITH_COMPILER% %TOXPYTHON% setup.py bdist_wheel" +artifacts: + - path: dist\* + diff --git a/ci/appveyor-bootstrap.ps1 b/ci/appveyor-bootstrap.ps1 index 1dd5342..e3d0f30 100644 --- a/ci/appveyor-bootstrap.ps1 +++ b/ci/appveyor-bootstrap.ps1 @@ -80,9 +80,10 @@ function InstallPackage ($python_home, $pkg) { function main () { InstallPython $env:PYTHON_VERSION $env:PYTHON_ARCH $env:PYTHON_HOME InstallPip $env:PYTHON_HOME - InstallPackage $env:PYTHON_HOME setuptools + InstallPackage $env:PYTHON_HOME "setuptools>=18.0.1" InstallPackage $env:PYTHON_HOME wheel InstallPackage $env:PYTHON_HOME tox + InstallPackage $env:PYTHON_HOME "virtualenv>=13.1.0" } main diff --git a/ci/bootstrap.py b/ci/bootstrap.py index cf93a54..466bb8f 100755 --- a/ci/bootstrap.py +++ b/ci/bootstrap.py @@ -6,21 +6,25 @@ import sys from os.path import exists from os.path import join +from os.path import dirname +from os.path import abspath if __name__ == "__main__": - base_path = join(".tox", "configure") + base_path = dirname(dirname(abspath(__file__))) + print("Project path: {0}".format(base_path)) + env_path = join(base_path, ".tox", "bootstrap") if sys.platform == "win32": - bin_path = join(base_path, "Scripts") + bin_path = join(env_path, "Scripts") else: - bin_path = join(base_path, "bin") - if not exists(base_path): + bin_path = join(env_path, "bin") + if not exists(env_path): import subprocess - print("Bootstrapping ...") + print("Making bootstrap env in: {0} ...".format(env_path)) try: - subprocess.check_call(["virtualenv", base_path]) + subprocess.check_call(["virtualenv", env_path]) except Exception: - subprocess.check_call([sys.executable, "-m", "virtualenv", base_path]) + subprocess.check_call([sys.executable, "-m", "virtualenv", env_path]) print("Installing `jinja2` and `matrix` into bootstrap environment ...") subprocess.check_call([join(bin_path, "pip"), "install", "jinja2", "matrix"]) activate = join(bin_path, "activate_this.py") @@ -30,13 +34,13 @@ import matrix jinja = jinja2.Environment( - loader=jinja2.FileSystemLoader(join("ci", "templates")), + loader=jinja2.FileSystemLoader(join(base_path, "ci", "templates")), trim_blocks=True, lstrip_blocks=True, keep_trailing_newline=True ) tox_environments = {} - for (alias, conf) in matrix.from_file("setup.cfg").items(): + for (alias, conf) in matrix.from_file(join(base_path, "setup.cfg")).items(): python = conf["python_versions"] deps = conf["dependencies"] if "coverage_flags" in conf: @@ -54,7 +58,7 @@ tox_environments[alias].update(env_vars=env_vars.split()) for name in os.listdir(join("ci", "templates")): - with open(name, "w") as fh: + with open(join(base_path, name), "w") as fh: fh.write(jinja.get_template(name).render(tox_environments=tox_environments)) print("Wrote {}".format(name)) print("DONE.") diff --git a/ci/templates/.travis.yml b/ci/templates/.travis.yml index 684b18d..34f68d5 100644 --- a/ci/templates/.travis.yml +++ b/ci/templates/.travis.yml @@ -7,20 +7,26 @@ env: matrix: - TOXENV=check {% for env, config in tox_environments|dictsort %} - - TOXENV={{ env }}{% if config.cover %},coveralls{% endif %} + - TOXENV={{ env }}{% if config.cover %},coveralls,codecov{% endif %} {% endfor %} before_install: - python --version - - virtualenv --version - - pip --version - uname -a - lsb_release -a install: - pip install tox + - virtualenv --version + - easy_install --version + - pip --version + - tox --version script: - tox -v +after_failure: + - more .tox/log/* | cat + - more .tox/*/log/* | cat notifications: email: on_success: never on_failure: always + diff --git a/ci/templates/appveyor.yml b/ci/templates/appveyor.yml index cc03b27..9dbdd76 100644 --- a/ci/templates/appveyor.yml +++ b/ci/templates/appveyor.yml @@ -1,20 +1,26 @@ -version: '{build}' +version: '{branch}-{build}' build: off environment: + global: + WITH_COMPILER: "cmd /E:ON /V:ON /C .\\ci\\appveyor-with-compiler.cmd" matrix: - TOXENV: check PYTHON_HOME: "C:\\Python27" PYTHON_VERSION: "2.7" PYTHON_ARCH: "32" {% for env, config in tox_environments|dictsort %}{% if env.startswith('2.7') or env.startswith('3.4') or env.startswith('3.3') %} - {% if config['cover'] %} - - TOXENV: "{{ env }}" + - TOXENV: "{{ env }}{% if not env.endswith('-nocover') %},codecov{% endif %}" TOXPYTHON: "C:\\Python{{ env[:3].replace('.', '') }}\\python.exe" WINDOWS_SDK_VERSION: "v7.{{ '1' if env[0] == '3' else '0' }}" PYTHON_HOME: "C:\\Python{{ env[:3].replace('.', '') }}" PYTHON_VERSION: "{{ env[:3] }}" PYTHON_ARCH: "32" - {% endif %} + - TOXENV: "{{ env }}{% if not env.endswith('-nocover') %},codecov{% endif %}" + TOXPYTHON: "C:\\Python{{ env[:3].replace('.', '') }}-x64\\python.exe" + WINDOWS_SDK_VERSION: "v7.{{ '1' if env[0] == '3' else '0' }}" + PYTHON_HOME: "C:\\Python{{ env[:3].replace('.', '') }}-x64" + PYTHON_VERSION: "{{ env[:3] }}" + PYTHON_ARCH: "64" {% endif %}{% endfor %} init: - "ECHO %TOXENV%" @@ -25,4 +31,9 @@ test_script: - "%PYTHON_HOME%\\Scripts\\tox --version" - "%PYTHON_HOME%\\Scripts\\virtualenv --version" - "%PYTHON_HOME%\\Scripts\\pip --version" - - "%PYTHON_HOME%\\Scripts\\tox" + - "%WITH_COMPILER% %PYTHON_HOME%\\Scripts\\tox" +after_test: + - "IF \"%TOXENV:~-8,8%\" == \"-nocover\" %WITH_COMPILER% %TOXPYTHON% setup.py bdist_wheel" +artifacts: + - path: dist\* + diff --git a/ci/templates/tox.ini b/ci/templates/tox.ini index a0971a1..0439819 100644 --- a/ci/templates/tox.ini +++ b/ci/templates/tox.ini @@ -12,6 +12,8 @@ envlist = setenv = PYTHONPATH={toxinidir}/tests PYTHONUNBUFFERED=yes +passenv = + * deps = pytest pytest-capturelog @@ -20,13 +22,14 @@ deps = mock tornado commands = - {posargs:py.test -vv} + {posargs:py.test -vv --ignore=src} [testenv:spell] setenv = - SPELLCHECK = 1 + SPELLCHECK=1 commands = sphinx-build -b spelling docs dist/docs +skip_install = true usedevelop = true deps = -r{toxinidir}/docs/requirements.txt @@ -34,23 +37,22 @@ deps = pyenchant [testenv:docs] -whitelist_externals = - rm -commands = - rm -rf dist/docs || rmdir /S /Q dist\docs - sphinx-build -b html docs dist/docs - sphinx-build -b linkcheck docs dist/docs -usedevelop = true deps = -r{toxinidir}/docs/requirements.txt +commands = + sphinx-build {posargs:-E} -b html docs dist/docs + sphinx-build -b linkcheck docs dist/docs -[testenv:configure] +[testenv:bootstrap] deps = jinja2 matrix -usedevelop = true +skip_install = true +usedevelop = false commands = - python bootstrap.py + python ci/bootstrap.py +passenv = + * [testenv:check] basepython = python3.4 @@ -60,37 +62,53 @@ deps = flake8 readme pygments -usedevelop = true +skip_install = true +usedevelop = false commands = python setup.py check --strict --metadata --restructuredtext check-manifest {toxinidir} - flake8 src + flake8 src tests [testenv:coveralls] deps = coveralls -usedevelop = true +skip_install = true +usedevelop = false commands = coverage combine coverage report - coveralls + coveralls [] + +[testenv:codecov] +deps = + codecov +skip_install = true +usedevelop = false +commands = + coverage combine + coverage report + coverage xml --ignore-errors + codecov [] + [testenv:report] basepython = python3.4 +deps = coverage +skip_install = true +usedevelop = false commands = coverage combine coverage report -usedevelop = true -deps = coverage [testenv:clean] commands = coverage erase -usedevelop = true +skip_install = true +usedevelop = false deps = coverage {% for env, config in tox_environments|dictsort %} [testenv:{{ env }}] -basepython = {{ config.python }} +basepython = {env:TOXPYTHON:{{ config.python }}} {% if config.cover or config.env_vars %} setenv = {[testenv]setenv} @@ -102,7 +120,7 @@ setenv = WITH_COVERAGE=yes usedevelop = true commands = - {posargs:py.test --cov=src --cov-report=term-missing -vv} + {posargs:py.test --cov --cov-report=term-missing -vv} {% endif %} {% if config.cover or config.deps %} deps = @@ -116,3 +134,5 @@ deps = {% endfor %} {% endfor %} + + diff --git a/docs/authors.rst b/docs/authors.rst new file mode 100644 index 0000000..e122f91 --- /dev/null +++ b/docs/authors.rst @@ -0,0 +1 @@ +.. include:: ../AUTHORS.rst diff --git a/docs/conf.py b/docs/conf.py index aa883db..4d4a17d 100644 --- a/docs/conf.py +++ b/docs/conf.py @@ -20,15 +20,17 @@ source_suffix = '.rst' master_doc = 'index' -project = u'aspectlib' -year = u'2015' -author = u'Ionel Cristian Maries' +project = 'Aspectlib' +year = '2014-2015' +author = 'Ionel Cristian Mărieș' copyright = '{0}, {1}'.format(year, author) -version = release = u'1.3.0' - +version = release = '1.3.0' import sphinx_py3doc_enhanced_theme html_theme = "sphinx_py3doc_enhanced_theme" html_theme_path = [sphinx_py3doc_enhanced_theme.get_html_theme_path()] +html_theme_options = { + 'githuburl': 'https://github.com/ionelmc/python-aspectlib/' +} pygments_style = 'trac' templates_path = ['.'] @@ -39,6 +41,3 @@ '**': ['searchbox.html', 'globaltoc.html', 'sourcelink.html'], } html_short_title = '%s-%s' % (project, version) -html_theme_options = { - 'githuburl': 'https://github.com/ionelmc/python-aspectlib/' -} diff --git a/docs/requirements.txt b/docs/requirements.txt index 7227a52..1632a96 100644 --- a/docs/requirements.txt +++ b/docs/requirements.txt @@ -1,4 +1,4 @@ -sphinx==1.3.1 -sphinxcontrib-napoleon==0.3.11 -sphinx-py3doc-enhanced-theme==2.2.1 +sphinx +sphinxcontrib-napoleon +sphinx-py3doc-enhanced-theme -e . diff --git a/setup.cfg b/setup.cfg index dc12e37..f1388ad 100644 --- a/setup.cfg +++ b/setup.cfg @@ -2,11 +2,11 @@ universal = 1 [aliases] -release = register clean --all sdist bdist_wheel upload +release = register clean --all sdist bdist_wheel [flake8] max-line-length = 140 -exclude = tests/* +exclude = tests/*,*/migrations/*,*/south_migrations/* [pytest] norecursedirs = @@ -15,18 +15,22 @@ norecursedirs = .env dist build + south_migrations + migrations python_files = test_*.py *_test.py tests.py addopts = - -rxEfs + -rxEfsw --strict - --ignore docs/conf.py - --ignore setup.py - --ignore bootstrap.py + --ignore=docs/conf.py + --ignore=setup.py + --ignore=ci + --ignore=.eggs --doctest-modules - --tb short + --doctest-glob=\*.rst + --tb=short [isort] force_single_line=True @@ -36,6 +40,26 @@ default_section=THIRDPARTY forced_separate=test_aspectlib [matrix] +# This is the configuration for the `./bootstrap.py` script. +# It generates `.travis.yml`, `tox.ini` and `appveyor.yml`. +# +# Syntax: [alias:] value [!variable[glob]] [&variable[glob]] +# +# alias: +# - is used to generate the tox environment +# - it's optional +# - if not present the alias will be computed from the `value` +# value: +# - a value of "-" means empty +# !variable[glob]: +# - exclude the combination of the current `value` with +# any value matching the `glob` in `variable` +# - can use as many you want +# &variable[glob]: +# - only include the combination of the current `value` +# when there's a value matching `glob` in `variable` +# - can use as many you want + python_versions = 2.6 2.7 diff --git a/setup.py b/setup.py index 7407aa9..b491a3a 100644 --- a/setup.py +++ b/setup.py @@ -28,7 +28,7 @@ def read(*names, **kwargs): description="aspectlib is an aspect-oriented programming, monkey-patch and decorators library. It is useful when " "changing behavior in existing code is desired. It includes tools for debugging and testing: simple " "mock/record and a complete capture/replay framework.", - long_description="%s\n%s" % (read('README.rst'), re.sub(':(obj|func):`~?(.*?)`', r'``\1``', read('docs', 'changelog.rst'))), + long_description='%s\n%s' % (read('README.rst'), re.sub(':[a-z]+:`~?(.*?)`', r'``\1``', read('CHANGELOG.rst'))), author='Ionel Cristian Mărieș', author_email='contact@ionelmc.ro', url='https://github.com/ionelmc/python-aspectlib', diff --git a/tox.ini b/tox.ini index 6bf86c6..2b776a3 100644 --- a/tox.ini +++ b/tox.ini @@ -29,6 +29,8 @@ envlist = setenv = PYTHONPATH={toxinidir}/tests PYTHONUNBUFFERED=yes +passenv = + * deps = pytest pytest-capturelog @@ -37,13 +39,14 @@ deps = mock tornado commands = - {posargs:py.test -vv} + {posargs:py.test -vv --ignore=src} [testenv:spell] setenv = - SPELLCHECK = 1 + SPELLCHECK=1 commands = sphinx-build -b spelling docs dist/docs +skip_install = true usedevelop = true deps = -r{toxinidir}/docs/requirements.txt @@ -51,23 +54,22 @@ deps = pyenchant [testenv:docs] -whitelist_externals = - rm -commands = - rm -rf dist/docs || rmdir /S /Q dist\docs - sphinx-build -b html docs dist/docs - sphinx-build -b linkcheck docs dist/docs -usedevelop = true deps = -r{toxinidir}/docs/requirements.txt +commands = + sphinx-build {posargs:-E} -b html docs dist/docs + sphinx-build -b linkcheck docs dist/docs -[testenv:configure] +[testenv:bootstrap] deps = jinja2 matrix -usedevelop = true +skip_install = true +usedevelop = false commands = - python bootstrap.py + python ci/bootstrap.py +passenv = + * [testenv:check] basepython = python3.4 @@ -77,69 +79,85 @@ deps = flake8 readme pygments -usedevelop = true +skip_install = true +usedevelop = false commands = python setup.py check --strict --metadata --restructuredtext check-manifest {toxinidir} - flake8 src + flake8 src tests [testenv:coveralls] deps = coveralls -usedevelop = true +skip_install = true +usedevelop = false commands = coverage combine coverage report - coveralls + coveralls [] + +[testenv:codecov] +deps = + codecov +skip_install = true +usedevelop = false +commands = + coverage combine + coverage report + coverage xml --ignore-errors + codecov [] + [testenv:report] basepython = python3.4 +deps = coverage +skip_install = true +usedevelop = false commands = coverage combine coverage report -usedevelop = true -deps = coverage [testenv:clean] commands = coverage erase -usedevelop = true +skip_install = true +usedevelop = false deps = coverage [testenv:2.6] -basepython = python2.6 +basepython = {env:TOXPYTHON:python2.6} setenv = {[testenv]setenv} WITH_COVERAGE=yes usedevelop = true commands = - {posargs:py.test --cov=src --cov-report=term-missing -vv} + {posargs:py.test --cov --cov-report=term-missing -vv} deps = {[testenv]deps} pytest-cov trollius [testenv:2.6-debug] -basepython = python2.6 +basepython = {env:TOXPYTHON:python2.6} setenv = {[testenv]setenv} ASPECTLIB_DEBUG=yes WITH_COVERAGE=yes usedevelop = true commands = - {posargs:py.test --cov=src --cov-report=term-missing -vv} + {posargs:py.test --cov --cov-report=term-missing -vv} deps = {[testenv]deps} pytest-cov trollius [testenv:2.6-nocover] -basepython = python2.6 +basepython = {env:TOXPYTHON:python2.6} deps = {[testenv]deps} trollius [testenv:2.6-nocover-debug] -basepython = python2.6 +basepython = {env:TOXPYTHON:python2.6} setenv = {[testenv]setenv} ASPECTLIB_DEBUG=yes @@ -148,40 +166,40 @@ deps = trollius [testenv:2.7] -basepython = python2.7 +basepython = {env:TOXPYTHON:python2.7} setenv = {[testenv]setenv} WITH_COVERAGE=yes usedevelop = true commands = - {posargs:py.test --cov=src --cov-report=term-missing -vv} + {posargs:py.test --cov --cov-report=term-missing -vv} deps = {[testenv]deps} pytest-cov trollius [testenv:2.7-debug] -basepython = python2.7 +basepython = {env:TOXPYTHON:python2.7} setenv = {[testenv]setenv} ASPECTLIB_DEBUG=yes WITH_COVERAGE=yes usedevelop = true commands = - {posargs:py.test --cov=src --cov-report=term-missing -vv} + {posargs:py.test --cov --cov-report=term-missing -vv} deps = {[testenv]deps} pytest-cov trollius [testenv:2.7-nocover] -basepython = python2.7 +basepython = {env:TOXPYTHON:python2.7} deps = {[testenv]deps} trollius [testenv:2.7-nocover-debug] -basepython = python2.7 +basepython = {env:TOXPYTHON:python2.7} setenv = {[testenv]setenv} ASPECTLIB_DEBUG=yes @@ -190,40 +208,40 @@ deps = trollius [testenv:3.3] -basepython = python3.3 +basepython = {env:TOXPYTHON:python3.3} setenv = {[testenv]setenv} WITH_COVERAGE=yes usedevelop = true commands = - {posargs:py.test --cov=src --cov-report=term-missing -vv} + {posargs:py.test --cov --cov-report=term-missing -vv} deps = {[testenv]deps} pytest-cov asyncio [testenv:3.3-debug] -basepython = python3.3 +basepython = {env:TOXPYTHON:python3.3} setenv = {[testenv]setenv} ASPECTLIB_DEBUG=yes WITH_COVERAGE=yes usedevelop = true commands = - {posargs:py.test --cov=src --cov-report=term-missing -vv} + {posargs:py.test --cov --cov-report=term-missing -vv} deps = {[testenv]deps} pytest-cov asyncio [testenv:3.3-nocover] -basepython = python3.3 +basepython = {env:TOXPYTHON:python3.3} deps = {[testenv]deps} asyncio [testenv:3.3-nocover-debug] -basepython = python3.3 +basepython = {env:TOXPYTHON:python3.3} setenv = {[testenv]setenv} ASPECTLIB_DEBUG=yes @@ -232,74 +250,74 @@ deps = asyncio [testenv:3.4] -basepython = python3.4 +basepython = {env:TOXPYTHON:python3.4} setenv = {[testenv]setenv} WITH_COVERAGE=yes usedevelop = true commands = - {posargs:py.test --cov=src --cov-report=term-missing -vv} + {posargs:py.test --cov --cov-report=term-missing -vv} deps = {[testenv]deps} pytest-cov [testenv:3.4-debug] -basepython = python3.4 +basepython = {env:TOXPYTHON:python3.4} setenv = {[testenv]setenv} ASPECTLIB_DEBUG=yes WITH_COVERAGE=yes usedevelop = true commands = - {posargs:py.test --cov=src --cov-report=term-missing -vv} + {posargs:py.test --cov --cov-report=term-missing -vv} deps = {[testenv]deps} pytest-cov [testenv:3.4-nocover] -basepython = python3.4 +basepython = {env:TOXPYTHON:python3.4} [testenv:3.4-nocover-debug] -basepython = python3.4 +basepython = {env:TOXPYTHON:python3.4} setenv = {[testenv]setenv} ASPECTLIB_DEBUG=yes [testenv:pypy] -basepython = pypy +basepython = {env:TOXPYTHON:pypy} setenv = {[testenv]setenv} WITH_COVERAGE=yes usedevelop = true commands = - {posargs:py.test --cov=src --cov-report=term-missing -vv} + {posargs:py.test --cov --cov-report=term-missing -vv} deps = {[testenv]deps} pytest-cov trollius [testenv:pypy-debug] -basepython = pypy +basepython = {env:TOXPYTHON:pypy} setenv = {[testenv]setenv} ASPECTLIB_DEBUG=yes WITH_COVERAGE=yes usedevelop = true commands = - {posargs:py.test --cov=src --cov-report=term-missing -vv} + {posargs:py.test --cov --cov-report=term-missing -vv} deps = {[testenv]deps} pytest-cov trollius [testenv:pypy-nocover] -basepython = pypy +basepython = {env:TOXPYTHON:pypy} deps = {[testenv]deps} trollius [testenv:pypy-nocover-debug] -basepython = pypy +basepython = {env:TOXPYTHON:pypy} setenv = {[testenv]setenv} ASPECTLIB_DEBUG=yes @@ -307,3 +325,5 @@ deps = {[testenv]deps} trollius + + From 1ffaa14289e97ccacbe4051800d60ba49e068eb1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ionel=20Cristian=20M=C4=83rie=C8=99?= Date: Sat, 12 Sep 2015 03:43:30 +0300 Subject: [PATCH 101/179] Fix result for generator wrapping aspects. Previously no return/raiseStopIteration would return empty tuple instead of none. --- src/aspectlib/__init__.py | 21 ++++++++++++++++++--- tests/test_aspectlib.py | 19 +++++++++++++++++++ tests/test_aspectlib_py3.py | 25 +++++++++++++++++++++++++ 3 files changed, 62 insertions(+), 3 deletions(-) create mode 100644 tests/test_aspectlib_py3.py diff --git a/src/aspectlib/__init__.py b/src/aspectlib/__init__.py index 4d5715f..76b708d 100644 --- a/src/aspectlib/__init__.py +++ b/src/aspectlib/__init__.py @@ -183,7 +183,12 @@ def advising_generator_wrapper(*args, **kwargs): generated = next(gen) except StopIteration as exc: logexception("The cutpoint has been exhausted (early).") - result = exc.args and exc.args[0] + result = exc.args + if result: + if len(result) == 1: + result = exc.args[0] + else: + result = None else: while True: try: @@ -198,7 +203,12 @@ def advising_generator_wrapper(*args, **kwargs): generated = gen.throw(*sys.exc_info()) except StopIteration as exc: logexception("The cutpoint has been exhausted.") - result = exc.args and exc.args[0] + result = exc.args + if result: + if len(result) == 1: + result = exc.args[0] + else: + result = None break else: try: @@ -208,7 +218,12 @@ def advising_generator_wrapper(*args, **kwargs): generated = gen.send(sent) except StopIteration as exc: logexception("The cutpoint has been exhausted.") - result = exc.args and exc.args[0] + result = exc.args + if result: + if len(result) == 1: + result = exc.args[0] + else: + result = None break except BaseException as exc: advice = advisor.throw(*sys.exc_info()) diff --git a/tests/test_aspectlib.py b/tests/test_aspectlib.py index 62a986e..eedf1b7 100644 --- a/tests/test_aspectlib.py +++ b/tests/test_aspectlib.py @@ -1563,3 +1563,22 @@ def test_weave_module(strmod=None): def test_weave_module_as_str(): test_weave_module("test_pkg1.test_pkg2.test_mod") + + +def test_aspect_chain_on_generator(): + @aspectlib.Aspect + def foo(arg): + result = yield aspectlib.Proceed(arg + 1) + yield aspectlib.Return(result - 1) + + @foo + @foo + @foo + def func(a): + assert a == 3 + raise StopIteration(a) + yield + + gen = func(0) + result = pytest.raises(StopIteration, gen.__next__ if hasattr(gen, '__next__') else gen.next) + assert result.value.args == (0,) diff --git a/tests/test_aspectlib_py3.py b/tests/test_aspectlib_py3.py new file mode 100644 index 0000000..13f99eb --- /dev/null +++ b/tests/test_aspectlib_py3.py @@ -0,0 +1,25 @@ +# encoding: utf8 +from __future__ import print_function + +import pytest + +import aspectlib + + +def test_aspect_chain_on_generator(): + @aspectlib.Aspect + def foo(arg): + result = yield aspectlib.Proceed(arg + 1) + yield aspectlib.Return(result - 1) + + @foo + @foo + @foo + def func(a): + assert a == 3 + return a + yield + + gen = func(0) + result = pytest.raises(StopIteration, gen.__next__ if hasattr(gen, '__next__') else gen.next) + assert result.value.args == (0,) From 520c0f92b1248629c0ad3ed6bedb1555e7103080 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ionel=20Cristian=20M=C4=83rie=C8=99?= Date: Sat, 12 Sep 2015 03:46:16 +0300 Subject: [PATCH 102/179] Reformat --- tests/test_aspectlib.py | 41 ++++++++++++++++++++++++++++++----------- 1 file changed, 30 insertions(+), 11 deletions(-) diff --git a/tests/test_aspectlib.py b/tests/test_aspectlib.py index eedf1b7..2801531 100644 --- a/tests/test_aspectlib.py +++ b/tests/test_aspectlib.py @@ -12,6 +12,8 @@ class Base(object): def meth(*_): return 'base' + + Base2 = Base @@ -21,11 +23,15 @@ class Sub(Base): class Global(Base): pass + + Global2 = Global class MissingGlobal(Base): pass + + AliasedGlobal = MissingGlobal del MissingGlobal @@ -36,6 +42,8 @@ def module_func(): def module_func2(): pass + + module_func3 = module_func2 @@ -322,7 +330,7 @@ def aspect(): @aspect def func(): - 1/0 + 1 / 0 assert func() == 'stuff' @@ -330,7 +338,7 @@ def func(): def test_aspect_raise_from_aspect(): @aspectlib.Aspect def aspect(): - 1/0 + 1 / 0 yield @aspect @@ -437,6 +445,7 @@ def test_weave_wrong_module(): {}) ] + def test_weave_no_aliases(): with aspectlib.weave(module_func2, mock('stuff'), aliases=False): assert module_func2() == 'stuff' @@ -569,7 +578,7 @@ def test_weave_subclass_meth_from_baseclass(): def aspect(*args): result = yield history.append(args + (result,)) - yield aspectlib.Return('bar-'+result) + yield aspectlib.Return('bar-' + result) with aspectlib.weave(NormalTestSubClass.only_in_base, aspect): inst = NormalTestSubClass('stuff') @@ -589,7 +598,7 @@ def test_weave_subclass_meth_from_baseclass_2_level(): def aspect(*args): result = yield history.append(args + (result,)) - yield aspectlib.Return('bar-'+result) + yield aspectlib.Return('bar-' + result) with aspectlib.weave(NormalTestSubSubClass.only_in_base, aspect): inst = NormalTestSubSubClass('stuff') @@ -609,7 +618,7 @@ def test_weave_legacy_subclass_meth_from_baseclass(): def aspect(*args): result = yield history.append(args + (result,)) - yield aspectlib.Return('bar-'+result) + yield aspectlib.Return('bar-' + result) with aspectlib.weave(LegacyTestSubClass.only_in_base, aspect): inst = LegacyTestSubClass('stuff') @@ -629,7 +638,7 @@ def test_weave_legacy_subclass_meth_from_baseclass_2_level(): def aspect(*args): result = yield history.append(args + (result,)) - yield aspectlib.Return('bar-'+result) + yield aspectlib.Return('bar-' + result) with aspectlib.weave(LegacyTestSubSubClass.only_in_base, aspect): inst = LegacyTestSubSubClass('stuff') @@ -867,7 +876,6 @@ def aspect(*args): inst = SlotsTestClass() with aspectlib.weave(SlotsTestClass, aspect, lazy=True): - inst = SlotsTestClass('stuff') assert inst.foo == 'stuff' assert inst.bar is None @@ -1146,7 +1154,7 @@ def aspect(): @aspect def func(): - 1/0 + 1 / 0 raises(ZeroDivisionError, func) @@ -1181,6 +1189,7 @@ def test_weave_subclass_meth_manual(): assert Sub().meth() == 'base' + @pytest.mark.skipif('aspectlib.PY3') def test_weave_subclass_meth_auto(): with aspectlib.weave(Sub.meth, mock('foobar'), lazy=True): @@ -1188,6 +1197,7 @@ def test_weave_subclass_meth_auto(): assert Sub().meth() == 'base' + @pytest.mark.skipif('aspectlib.PY2') def test_weave_subclass_meth_auto2(): with aspectlib.weave(Sub.meth, mock('foobar')): @@ -1216,6 +1226,7 @@ def test_sentinel(): def _internal(): pass + if aspectlib.PY3: exec(u"""# encoding: utf8 @@ -1287,6 +1298,7 @@ def aspect(): @aspect def func(): raise RuntimeError() + assert func() == 'squelched' assert hist == ['before', 'error', 'finally', 'closed'] @@ -1304,6 +1316,7 @@ def func(): raises(aspectlib.UnacceptableAdvice, func) + def test_aspect_on_generator_func(): hist = [] @@ -1331,8 +1344,9 @@ def func(): for i in range(3): yield i raise RuntimeError() + assert list(func()) == [0, 1, 2] - print (hist) + print(hist) assert hist == ['before', 'error', 'finally', 'closed'] @@ -1381,6 +1395,7 @@ def func(): def test_aspect_on_generator_close(): excs = [] + @aspectlib.Aspect def aspect(): yield aspectlib.Proceed @@ -1404,6 +1419,7 @@ def func(): def test_aspect_on_generator_throw(): excs = [] + @aspectlib.Aspect def aspect(): yield aspectlib.Proceed @@ -1427,6 +1443,7 @@ def func(): def test_aspect_on_generator_throw_exhaust(): excs = [] + @aspectlib.Aspect def aspect(): yield aspectlib.Proceed @@ -1492,6 +1509,7 @@ def func(): def test_aspect_on_generator_result(): result = [] + @aspectlib.Aspect def aspect(): result.append((yield aspectlib.Proceed)) @@ -1535,6 +1553,7 @@ def func(): val = yield val + 1 print("GOT", val) raise StopIteration("the-return-value") + gen = func() data = [] try: @@ -1544,11 +1563,10 @@ def func(): data.append('done') print(data) assert data == [100, 1, 2, 'done'], hist - print (hist) + print(hist) assert hist == ['before', 'the-return-value', 'after', 'finally', 'closed'] - def test_weave_module(strmod=None): calls = [] from test_pkg1.test_pkg2 import test_mod @@ -1561,6 +1579,7 @@ def test_weave_module(strmod=None): (obj, 'test_pkg1.test_pkg2.test_mod.meth', (), {}) ] + def test_weave_module_as_str(): test_weave_module("test_pkg1.test_pkg2.test_mod") From d481c9c949f0e62ddf00001d629f83abefc6f2a3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ionel=20Cristian=20M=C4=83rie=C8=99?= Date: Sat, 12 Sep 2015 04:17:06 +0300 Subject: [PATCH 103/179] More test. --- tests/test_aspectlib.py | 23 +++++++++++++ tests/test_aspectlib_py3.py | 64 +++++++++++++++++++++++++++++++++++++ 2 files changed, 87 insertions(+) diff --git a/tests/test_aspectlib.py b/tests/test_aspectlib.py index 2801531..195f93d 100644 --- a/tests/test_aspectlib.py +++ b/tests/test_aspectlib.py @@ -1601,3 +1601,26 @@ def func(a): gen = func(0) result = pytest.raises(StopIteration, gen.__next__ if hasattr(gen, '__next__') else gen.next) assert result.value.args == (0,) + + +def test_aspect_chain_on_generator_no_return(): + @aspectlib.Aspect + def foo(arg): + result = yield aspectlib.Proceed(arg + 1) + yield aspectlib.Return(result) + + @foo + @foo + @foo + def func(a): + assert a == 3 + yield + + gen = func(0) + if hasattr(gen, '__next__'): + assert gen.__next__() is None + result = pytest.raises(StopIteration, gen.__next__) + else: + assert gen.next() is None + result = pytest.raises(StopIteration, gen.next) + assert result.value.args == (None,) diff --git a/tests/test_aspectlib_py3.py b/tests/test_aspectlib_py3.py index 13f99eb..63d4e1d 100644 --- a/tests/test_aspectlib_py3.py +++ b/tests/test_aspectlib_py3.py @@ -23,3 +23,67 @@ def func(a): gen = func(0) result = pytest.raises(StopIteration, gen.__next__ if hasattr(gen, '__next__') else gen.next) assert result.value.args == (0,) + + +def test_aspect_chain_on_generator_no_return(): + @aspectlib.Aspect + def foo(arg): + result = yield aspectlib.Proceed(arg + 1) + yield aspectlib.Return(result) + + @foo + @foo + @foo + def func(a): + assert a == 3 + yield + + gen = func(0) + assert gen.__next__() is None + result = pytest.raises(StopIteration, gen.__next__) + assert result.value.args == (None,) + + +def consume(gen): + ret = [] + + def it(): + ret.append((yield from gen)) + + list(it()) + return ret[0] + + +def test_aspect_chain_on_generator_yield_from(): + @aspectlib.Aspect + def foo(arg): + result = yield aspectlib.Proceed(arg + 1) + yield aspectlib.Return(result - 1) + + @foo + @foo + @foo + def func(a): + assert a == 3 + return a + yield + + gen = func(0) + assert consume(func(0)) == 0 + + +def test_aspect_chain_on_generator_no_return_yield_from(): + @aspectlib.Aspect + def foo(arg): + result = yield aspectlib.Proceed(arg + 1) + yield aspectlib.Return(result) + + @foo + @foo + @foo + def func(a): + assert a == 3 + yield + + gen = func(0) + assert consume(func(0)) is None From 518c35244152135d17506c25a1990cc0defb43f6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ionel=20Cristian=20M=C4=83rie=C8=99?= Date: Sat, 12 Sep 2015 04:17:59 +0300 Subject: [PATCH 104/179] Remove old changelog. --- docs/changelog.rst | 141 +-------------------------------------------- 1 file changed, 1 insertion(+), 140 deletions(-) diff --git a/docs/changelog.rst b/docs/changelog.rst index 3cf875d..565b052 100644 --- a/docs/changelog.rst +++ b/docs/changelog.rst @@ -1,140 +1 @@ -Changelog -========= - -1.3.0 (2015-06-06) ------------------- - -* Added ``messages`` property to :obj:`aspectlib.test.LogCapture`. Change ``call`` to have level name instead of number. -* Fixed a bogus warning from :func:`aspectlib.patch_module`` when patching methods on old style classes. - -1.2.2 (2014-11-25) ------------------- - -* Added support for weakrefs in the ``__logged__`` wrapper from :obj:`aspectlib.debug.log` decorator. - -1.2.1 (2014-10-15) ------------------- - -* Don't raise exceptions from ``Replay.__exit__`` if there would be an error (makes original cause hard to debug). - -1.2.0 (2014-06-24) ------------------- - -* Fixed weaving methods that were defined in some baseclass (and not on the target class). -* Fixed wrong qualname beeing used in the Story/Replay recording. Now used the alias given to the weaver instead of - whatever is the realname on the current platform. - -1.1.1 (2014-06-14) ------------------- - -* Use ``ASPECTLIB_DEBUG`` for every logger in ``aspectlib``. - -1.1.0 (2014-06-13) ------------------- - -* Added a `bind` option to :obj:`aspectlib.Aspect` so you can access the cutpoint from the advisor. -* Replaced automatic importing in :obj:`aspectlib.test.Replay` with extraction of context variables (locals and globals - from the calling :obj:`aspectlib.test.Story`). Works better than the previous inference of module from AST of the - result. -* All the methods on the replay are now properties: :obj:`aspectlib.test.Story.diff`, - :obj:`aspectlib.test.Story.unexpected` and :obj:`aspectlib.test.Story.missing`. -* Added :obj:`aspectlib.test.Story.actual` and :obj:`aspectlib.test.Story.expected`. -* Added an ``ASPECTLIB_DEBUG`` environment variable option to switch on debug logging in ``aspectlib``'s internals. - -1.0.0 (2014-05-03) ------------------- - -* Reworked the internals :obj:`aspectlib.test.Story` to keep call ordering, to allow dependencies and improved the - serialization (used in the diffs and the missing/unexpected lists). - - -0.9.0 (2014-04-16) ------------------- - -* Changed :obj:`aspectlib.test.record`: - - * Renamed `history` option to `calls`. - * Renamed `call` option to `iscalled`. - * Added `callback` option. - * Added `extended` option. - -* Changed :obj:`aspectlib.weave`: - - * Allow weaving everything in a module. - * Allow weaving instances of new-style classes. - -* Added :obj:`aspectlib.test.Story` class for capture-replay and stub/mock testing. - -0.8.1 (2014-04-01) ------------------- - -* Use simpler import for the py3support. - -0.8.0 (2014-03-31) ------------------- - -* Change :obj:`aspectlib.debug.log` to use :obj:`~aspectlib.Aspect` and work as expected with coroutines or generators. -* Fixed :obj:`aspectlib.debug.log` to work on Python 3.4. -* Remove the undocumented ``aspectlib.Yield`` advice. It was only usable when decorating generators. - -0.7.0 (2014-03-28) ------------------- - -* Add support for decorating generators and coroutines in :obj:`~aspectlib.Aspect`. -* Made aspectlib raise better exceptions. - -0.6.1 (2014-03-22) ------------------- - -* Fix checks inside :obj:`aspectlib.debug.log` that would inadvertently call ``__bool__``/``__nonzero``. - -0.6.0 (2014-03-17) ------------------- - -* Don't include __getattribute__ in ALL_METHODS - it's too dangerous dangerous dangerous dangerous dangerous dangerous - ... ;) -* Do a more reliable check for old-style classes in debug.log -* When weaving a class don't weave attributes that are callable but are not actually routines (functions, methods etc) - -0.5.0 (2014-03-16) ------------------- - -* Changed :obj:`aspectlib.debug.log`: - - * Renamed `arguments` to `call_args`. - * Renamed `arguments_repr` to `call_args_repr`. - * Added `call` option. - * Fixed issue with logging from old-style methods (object name was a generic "instance"). - -* Fixed issues with weaving some types of builtin methods. -* Allow to apply multiple aspects at the same time. -* Validate string targets before weaving. ``aspectlib.weave('mod.invalid name', aspect)`` now gives a clear error - (``invalid name`` is not a valid identifier) -* Various documentation improvements and examples. - -0.4.1 (2014-03-08) ------------------- - -* Remove junk from 0.4.0's source distribution. - -0.4.0 (2014-03-08) ------------------- - -* Changed :obj:`aspectlib.weave`: - - * Replaced `only_methods`, `skip_methods`, `skip_magicmethods` options with `methods`. - * Renamed `on_init` option to `lazy`. - * Added `aliases` option. - * Replaced `skip_subclasses` option with `subclasses`. - -* Fixed weaving methods from a string target. - -0.3.1 (2014-03-05) ------------------- - -* ??? - -0.3.0 (2014-03-05) ------------------- - -* First public release. +.. include:: ../CHANGELOG.rst From b67b34a62fb45dcd30bdf36ba00107d82088a53d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ionel=20Cristian=20M=C4=83rie=C8=99?= Date: Sat, 12 Sep 2015 04:40:39 +0300 Subject: [PATCH 105/179] Correct manifest. --- MANIFEST.in | 1 + 1 file changed, 1 insertion(+) diff --git a/MANIFEST.in b/MANIFEST.in index 3af7c27..6b1d0f2 100644 --- a/MANIFEST.in +++ b/MANIFEST.in @@ -7,6 +7,7 @@ graft tests include *.komodoproject include .bumpversion.cfg include .coveragerc +include .cookiecutterrc include .isort.cfg include .pylintrc From 7d0c6e932cec887d623a347ec241424e594f0c8d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ionel=20Cristian=20M=C4=83rie=C8=99?= Date: Sat, 12 Sep 2015 04:41:58 +0300 Subject: [PATCH 106/179] Restore changelog. --- CHANGELOG.rst | 138 ++++++++++++++++++++++++++++++++++++++++++++++++-- 1 file changed, 135 insertions(+), 3 deletions(-) diff --git a/CHANGELOG.rst b/CHANGELOG.rst index d7d7d48..3cf875d 100644 --- a/CHANGELOG.rst +++ b/CHANGELOG.rst @@ -1,8 +1,140 @@ - Changelog ========= 1.3.0 (2015-06-06) ------------------------------------------ +------------------ + +* Added ``messages`` property to :obj:`aspectlib.test.LogCapture`. Change ``call`` to have level name instead of number. +* Fixed a bogus warning from :func:`aspectlib.patch_module`` when patching methods on old style classes. + +1.2.2 (2014-11-25) +------------------ + +* Added support for weakrefs in the ``__logged__`` wrapper from :obj:`aspectlib.debug.log` decorator. + +1.2.1 (2014-10-15) +------------------ + +* Don't raise exceptions from ``Replay.__exit__`` if there would be an error (makes original cause hard to debug). + +1.2.0 (2014-06-24) +------------------ + +* Fixed weaving methods that were defined in some baseclass (and not on the target class). +* Fixed wrong qualname beeing used in the Story/Replay recording. Now used the alias given to the weaver instead of + whatever is the realname on the current platform. + +1.1.1 (2014-06-14) +------------------ + +* Use ``ASPECTLIB_DEBUG`` for every logger in ``aspectlib``. + +1.1.0 (2014-06-13) +------------------ + +* Added a `bind` option to :obj:`aspectlib.Aspect` so you can access the cutpoint from the advisor. +* Replaced automatic importing in :obj:`aspectlib.test.Replay` with extraction of context variables (locals and globals + from the calling :obj:`aspectlib.test.Story`). Works better than the previous inference of module from AST of the + result. +* All the methods on the replay are now properties: :obj:`aspectlib.test.Story.diff`, + :obj:`aspectlib.test.Story.unexpected` and :obj:`aspectlib.test.Story.missing`. +* Added :obj:`aspectlib.test.Story.actual` and :obj:`aspectlib.test.Story.expected`. +* Added an ``ASPECTLIB_DEBUG`` environment variable option to switch on debug logging in ``aspectlib``'s internals. + +1.0.0 (2014-05-03) +------------------ + +* Reworked the internals :obj:`aspectlib.test.Story` to keep call ordering, to allow dependencies and improved the + serialization (used in the diffs and the missing/unexpected lists). + + +0.9.0 (2014-04-16) +------------------ + +* Changed :obj:`aspectlib.test.record`: + + * Renamed `history` option to `calls`. + * Renamed `call` option to `iscalled`. + * Added `callback` option. + * Added `extended` option. + +* Changed :obj:`aspectlib.weave`: + + * Allow weaving everything in a module. + * Allow weaving instances of new-style classes. + +* Added :obj:`aspectlib.test.Story` class for capture-replay and stub/mock testing. + +0.8.1 (2014-04-01) +------------------ + +* Use simpler import for the py3support. + +0.8.0 (2014-03-31) +------------------ + +* Change :obj:`aspectlib.debug.log` to use :obj:`~aspectlib.Aspect` and work as expected with coroutines or generators. +* Fixed :obj:`aspectlib.debug.log` to work on Python 3.4. +* Remove the undocumented ``aspectlib.Yield`` advice. It was only usable when decorating generators. + +0.7.0 (2014-03-28) +------------------ + +* Add support for decorating generators and coroutines in :obj:`~aspectlib.Aspect`. +* Made aspectlib raise better exceptions. + +0.6.1 (2014-03-22) +------------------ + +* Fix checks inside :obj:`aspectlib.debug.log` that would inadvertently call ``__bool__``/``__nonzero``. + +0.6.0 (2014-03-17) +------------------ + +* Don't include __getattribute__ in ALL_METHODS - it's too dangerous dangerous dangerous dangerous dangerous dangerous + ... ;) +* Do a more reliable check for old-style classes in debug.log +* When weaving a class don't weave attributes that are callable but are not actually routines (functions, methods etc) + +0.5.0 (2014-03-16) +------------------ + +* Changed :obj:`aspectlib.debug.log`: + + * Renamed `arguments` to `call_args`. + * Renamed `arguments_repr` to `call_args_repr`. + * Added `call` option. + * Fixed issue with logging from old-style methods (object name was a generic "instance"). + +* Fixed issues with weaving some types of builtin methods. +* Allow to apply multiple aspects at the same time. +* Validate string targets before weaving. ``aspectlib.weave('mod.invalid name', aspect)`` now gives a clear error + (``invalid name`` is not a valid identifier) +* Various documentation improvements and examples. + +0.4.1 (2014-03-08) +------------------ + +* Remove junk from 0.4.0's source distribution. + +0.4.0 (2014-03-08) +------------------ + +* Changed :obj:`aspectlib.weave`: + + * Replaced `only_methods`, `skip_methods`, `skip_magicmethods` options with `methods`. + * Renamed `on_init` option to `lazy`. + * Added `aliases` option. + * Replaced `skip_subclasses` option with `subclasses`. + +* Fixed weaving methods from a string target. + +0.3.1 (2014-03-05) +------------------ + +* ??? + +0.3.0 (2014-03-05) +------------------ -* First release on PyPI. +* First public release. From 0049a5f1a645349085759de3c35da046ef6a138d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ionel=20Cristian=20M=C4=83rie=C8=99?= Date: Sat, 12 Sep 2015 04:42:54 +0300 Subject: [PATCH 107/179] Update changelog. --- CHANGELOG.rst | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/CHANGELOG.rst b/CHANGELOG.rst index 3cf875d..430db93 100644 --- a/CHANGELOG.rst +++ b/CHANGELOG.rst @@ -1,6 +1,11 @@ Changelog ========= +1.3.1 (2015-09-12) +------------------ + +* Corrected result handling when using Aspects on generators. + 1.3.0 (2015-06-06) ------------------ From ca793bb915da894f9ce98ad82b14d481fda4cede Mon Sep 17 00:00:00 2001 From: Alexander Artemenko Date: Sat, 12 Sep 2015 09:47:07 +0300 Subject: [PATCH 108/179] =?UTF-8?q?Fix=20a=20typo=20"kids"=20=E2=80=93>=20?= =?UTF-8?q?"kinds"?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- docs/rationale.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/rationale.rst b/docs/rationale.rst index e422cf4..7fa9bce 100644 --- a/docs/rationale.rst +++ b/docs/rationale.rst @@ -9,7 +9,7 @@ There are perfectly sane use cases for monkey-patching (aka *weaving*): Then in those situations: -* You would need to handle yourself all different kids of patching (patching +* You would need to handle yourself all different kinds of patching (patching a module is different than patching a class, a function or a method for that matter). ``aspectlib`` will handle all this gross patching mumbo-jumbo for you, consistently, over many Python versions. * Writing the actual wrappers is repetitive, boring and error-prone. You can't reuse wrappers From 6050a2b58f75adbc3ad3f815d9761bc672e27634 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ionel=20Cristian=20M=C4=83rie=C8=99?= Date: Sat, 12 Sep 2015 13:14:22 +0300 Subject: [PATCH 109/179] Fix appveyor conf. --- appveyor.yml | 12 ++++++------ ci/templates/appveyor.yml | 4 ++-- 2 files changed, 8 insertions(+), 8 deletions(-) diff --git a/appveyor.yml b/appveyor.yml index d2a893c..f916898 100644 --- a/appveyor.yml +++ b/appveyor.yml @@ -44,13 +44,13 @@ environment: PYTHON_HOME: "C:\\Python27-x64" PYTHON_VERSION: "2.7" PYTHON_ARCH: "64" - - TOXENV: "2.7-nocover-debug,codecov" + - TOXENV: "2.7-nocover-debug" TOXPYTHON: "C:\\Python27\\python.exe" WINDOWS_SDK_VERSION: "v7.0" PYTHON_HOME: "C:\\Python27" PYTHON_VERSION: "2.7" PYTHON_ARCH: "32" - - TOXENV: "2.7-nocover-debug,codecov" + - TOXENV: "2.7-nocover-debug" TOXPYTHON: "C:\\Python27-x64\\python.exe" WINDOWS_SDK_VERSION: "v7.0" PYTHON_HOME: "C:\\Python27-x64" @@ -92,13 +92,13 @@ environment: PYTHON_HOME: "C:\\Python33-x64" PYTHON_VERSION: "3.3" PYTHON_ARCH: "64" - - TOXENV: "3.3-nocover-debug,codecov" + - TOXENV: "3.3-nocover-debug" TOXPYTHON: "C:\\Python33\\python.exe" WINDOWS_SDK_VERSION: "v7.1" PYTHON_HOME: "C:\\Python33" PYTHON_VERSION: "3.3" PYTHON_ARCH: "32" - - TOXENV: "3.3-nocover-debug,codecov" + - TOXENV: "3.3-nocover-debug" TOXPYTHON: "C:\\Python33-x64\\python.exe" WINDOWS_SDK_VERSION: "v7.1" PYTHON_HOME: "C:\\Python33-x64" @@ -140,13 +140,13 @@ environment: PYTHON_HOME: "C:\\Python34-x64" PYTHON_VERSION: "3.4" PYTHON_ARCH: "64" - - TOXENV: "3.4-nocover-debug,codecov" + - TOXENV: "3.4-nocover-debug" TOXPYTHON: "C:\\Python34\\python.exe" WINDOWS_SDK_VERSION: "v7.1" PYTHON_HOME: "C:\\Python34" PYTHON_VERSION: "3.4" PYTHON_ARCH: "32" - - TOXENV: "3.4-nocover-debug,codecov" + - TOXENV: "3.4-nocover-debug" TOXPYTHON: "C:\\Python34-x64\\python.exe" WINDOWS_SDK_VERSION: "v7.1" PYTHON_HOME: "C:\\Python34-x64" diff --git a/ci/templates/appveyor.yml b/ci/templates/appveyor.yml index 9dbdd76..0d1ea53 100644 --- a/ci/templates/appveyor.yml +++ b/ci/templates/appveyor.yml @@ -9,13 +9,13 @@ environment: PYTHON_VERSION: "2.7" PYTHON_ARCH: "32" {% for env, config in tox_environments|dictsort %}{% if env.startswith('2.7') or env.startswith('3.4') or env.startswith('3.3') %} - - TOXENV: "{{ env }}{% if not env.endswith('-nocover') %},codecov{% endif %}" + - TOXENV: "{{ env }}{% if config.cover %},codecov{% endif %}" TOXPYTHON: "C:\\Python{{ env[:3].replace('.', '') }}\\python.exe" WINDOWS_SDK_VERSION: "v7.{{ '1' if env[0] == '3' else '0' }}" PYTHON_HOME: "C:\\Python{{ env[:3].replace('.', '') }}" PYTHON_VERSION: "{{ env[:3] }}" PYTHON_ARCH: "32" - - TOXENV: "{{ env }}{% if not env.endswith('-nocover') %},codecov{% endif %}" + - TOXENV: "{{ env }}{% if config.cover %},codecov{% endif %}" TOXPYTHON: "C:\\Python{{ env[:3].replace('.', '') }}-x64\\python.exe" WINDOWS_SDK_VERSION: "v7.{{ '1' if env[0] == '3' else '0' }}" PYTHON_HOME: "C:\\Python{{ env[:3].replace('.', '') }}-x64" From f49745174dbcace5408f920d4f1198ba8f8e7026 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ionel=20Cristian=20M=C4=83rie=C8=99?= Date: Sat, 12 Sep 2015 13:47:01 +0300 Subject: [PATCH 110/179] Udate badges, manifest etc. --- MANIFEST.in | 3 +-- README.rst | 57 +++++++++++++++++++++++++++++++------------ docs/installation.rst | 3 ++- 3 files changed, 44 insertions(+), 19 deletions(-) diff --git a/MANIFEST.in b/MANIFEST.in index 6b1d0f2..1a3e36c 100644 --- a/MANIFEST.in +++ b/MANIFEST.in @@ -4,7 +4,6 @@ graft src graft ci graft tests -include *.komodoproject include .bumpversion.cfg include .coveragerc include .cookiecutterrc @@ -20,6 +19,6 @@ include README.rst include tox.ini .travis.yml appveyor.yml include conftest.py -global-exclude *.py[co] __pycache__ +global-exclude *.py[cod] __pycache__ *.so *.dylib prune .idea diff --git a/README.rst b/README.rst index e55dfc7..bf53367 100644 --- a/README.rst +++ b/README.rst @@ -1,58 +1,83 @@ -================ -python-aspectlib -================ - -| |docs| |travis| |appveyor| |coveralls| |landscape| |scrutinizer| -| |version| |downloads| |wheel| |supported-versions| |supported-implementations| +========= +Aspectlib +========= + +.. list-table:: + :stub-columns: 1 + + * - docs + - |docs| + * - tests + - | |travis| |appveyor| |requires| + | |coveralls| |codecov| + | |landscape| |scrutinizer| |codacy| |codeclimate| + * - package + - |version| |downloads| |wheel| |supported-versions| |supported-implementations| .. |docs| image:: https://readthedocs.org/projects/python-aspectlib/badge/?style=flat :target: https://readthedocs.org/projects/python-aspectlib :alt: Documentation Status -.. |travis| image:: http://img.shields.io/travis/ionelmc/python-aspectlib/master.png?style=flat +.. |travis| image:: https://travis-ci.org/ionelmc/python-aspectlib.svg?branch=master :alt: Travis-CI Build Status :target: https://travis-ci.org/ionelmc/python-aspectlib -.. |appveyor| image:: https://ci.appveyor.com/api/projects/status/github/ionelmc/python-aspectlib?branch=master +.. |appveyor| image:: https://ci.appveyor.com/api/projects/status/github/ionelmc/python-aspectlib?branch=master&svg=true :alt: AppVeyor Build Status :target: https://ci.appveyor.com/project/ionelmc/python-aspectlib -.. |coveralls| image:: http://img.shields.io/coveralls/ionelmc/python-aspectlib/master.png?style=flat +.. |requires| image:: https://requires.io/github/ionelmc/python-aspectlib/requirements.svg?branch=master + :alt: Requirements Status + :target: https://requires.io/github/ionelmc/python-aspectlib/requirements/?branch=master + +.. |coveralls| image:: https://coveralls.io/repos/ionelmc/python-aspectlib/badge.svg?branch=master&service=github :alt: Coverage Status :target: https://coveralls.io/r/ionelmc/python-aspectlib +.. |codecov| image:: https://codecov.io/github/ionelmc/python-aspectlib/coverage.svg?branch=master + :alt: Coverage Status + :target: https://codecov.io/github/ionelmc/python-aspectlib + .. |landscape| image:: https://landscape.io/github/ionelmc/python-aspectlib/master/landscape.svg?style=flat :target: https://landscape.io/github/ionelmc/python-aspectlib/master :alt: Code Quality Status -.. |version| image:: http://img.shields.io/pypi/v/aspectlib.png?style=flat +.. |codacy| image:: https://img.shields.io/codacy/REPLACE_WITH_PROJECT_ID.svg?style=flat + :target: https://www.codacy.com/app/ionelmc/python-aspectlib + :alt: Codacy Code Quality Status + +.. |codeclimate| image:: https://codeclimate.com/github/ionelmc/python-aspectlib/badges/gpa.svg + :target: https://codeclimate.com/github/ionelmc/python-aspectlib + :alt: CodeClimate Quality Status +.. |version| image:: https://img.shields.io/pypi/v/aspectlib.svg?style=flat :alt: PyPI Package latest release :target: https://pypi.python.org/pypi/aspectlib -.. |downloads| image:: http://img.shields.io/pypi/dm/aspectlib.png?style=flat +.. |downloads| image:: https://img.shields.io/pypi/dm/aspectlib.svg?style=flat :alt: PyPI Package monthly downloads :target: https://pypi.python.org/pypi/aspectlib -.. |wheel| image:: https://pypip.in/wheel/aspectlib/badge.png?style=flat +.. |wheel| image:: https://img.shields.io/pypi/wheel/aspectlib.svg?style=flat :alt: PyPI Wheel :target: https://pypi.python.org/pypi/aspectlib -.. |supported-versions| image:: https://pypip.in/py_versions/aspectlib/badge.png?style=flat +.. |supported-versions| image:: https://img.shields.io/pypi/pyversions/aspectlib.svg?style=flat :alt: Supported versions :target: https://pypi.python.org/pypi/aspectlib -.. |supported-implementations| image:: https://pypip.in/implementation/aspectlib/badge.png?style=flat +.. |supported-implementations| image:: https://img.shields.io/pypi/implementation/aspectlib.svg?style=flat :alt: Supported imlementations :target: https://pypi.python.org/pypi/aspectlib -.. |scrutinizer| image:: https://img.shields.io/scrutinizer/g/ionelmc/python-aspectlib/master.png?style=flat - :alt: Scrtinizer Status +.. |scrutinizer| image:: https://img.shields.io/scrutinizer/g/ionelmc/python-aspectlib/master.svg?style=flat + :alt: Scrutinizer Status :target: https://scrutinizer-ci.com/g/ionelmc/python-aspectlib/ ``aspectlib`` is an aspect-oriented programming, monkey-patch and decorators library. It is useful when changing behavior in existing code is desired. It includes tools for debugging and testing: simple mock/record and a complete capture/replay framework. +* Free software: BSD license Documentation ============= diff --git a/docs/installation.rst b/docs/installation.rst index a1074ff..6ac0382 100644 --- a/docs/installation.rst +++ b/docs/installation.rst @@ -1,7 +1,8 @@ +============ Installation ============ -:: +At the command line:: pip install aspectlib From 4b6575e0351ac25be245adc65408eef76c3981dc Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ionel=20Cristian=20M=C4=83rie=C8=99?= Date: Sat, 12 Sep 2015 13:49:06 +0300 Subject: [PATCH 111/179] Use correct project id. --- README.rst | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/README.rst b/README.rst index bf53367..6c560a4 100644 --- a/README.rst +++ b/README.rst @@ -42,13 +42,14 @@ Aspectlib :target: https://landscape.io/github/ionelmc/python-aspectlib/master :alt: Code Quality Status -.. |codacy| image:: https://img.shields.io/codacy/REPLACE_WITH_PROJECT_ID.svg?style=flat +.. |codacy| image:: https://img.shields.io/codacy/9557dc3ca38f43bcac85240f73e1985a.svg?style=flat :target: https://www.codacy.com/app/ionelmc/python-aspectlib :alt: Codacy Code Quality Status .. |codeclimate| image:: https://codeclimate.com/github/ionelmc/python-aspectlib/badges/gpa.svg :target: https://codeclimate.com/github/ionelmc/python-aspectlib :alt: CodeClimate Quality Status + .. |version| image:: https://img.shields.io/pypi/v/aspectlib.svg?style=flat :alt: PyPI Package latest release :target: https://pypi.python.org/pypi/aspectlib From eec26a25bfd6c0c82281739214db94721da40e55 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ionel=20Cristian=20M=C4=83rie=C8=99?= Date: Sat, 12 Sep 2015 14:38:20 +0300 Subject: [PATCH 112/179] =?UTF-8?q?Bump=20version:=201.3.0=20=E2=86=92=201?= =?UTF-8?q?.3.1?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .bumpversion.cfg | 5 ++++- docs/conf.py | 2 +- setup.py | 2 +- src/aspectlib/__init__.py | 2 +- 4 files changed, 7 insertions(+), 4 deletions(-) diff --git a/.bumpversion.cfg b/.bumpversion.cfg index 57515e8..8f0eba2 100644 --- a/.bumpversion.cfg +++ b/.bumpversion.cfg @@ -1,8 +1,11 @@ [bumpversion] -current_version = 1.3.0 +current_version = 1.3.1 commit = True tag = True [bumpversion:file:setup.py] + [bumpversion:file:docs/conf.py] + [bumpversion:file:src/aspectlib/__init__.py] + diff --git a/docs/conf.py b/docs/conf.py index 4d4a17d..57f1dee 100644 --- a/docs/conf.py +++ b/docs/conf.py @@ -24,7 +24,7 @@ year = '2014-2015' author = 'Ionel Cristian Mărieș' copyright = '{0}, {1}'.format(year, author) -version = release = '1.3.0' +version = release = '1.3.1' import sphinx_py3doc_enhanced_theme html_theme = "sphinx_py3doc_enhanced_theme" html_theme_path = [sphinx_py3doc_enhanced_theme.get_html_theme_path()] diff --git a/setup.py b/setup.py index b491a3a..70b59ff 100644 --- a/setup.py +++ b/setup.py @@ -23,7 +23,7 @@ def read(*names, **kwargs): setup( name='aspectlib', - version='1.3.0', + version='1.3.1', license='BSD', description="aspectlib is an aspect-oriented programming, monkey-patch and decorators library. It is useful when " "changing behavior in existing code is desired. It includes tools for debugging and testing: simple " diff --git a/src/aspectlib/__init__.py b/src/aspectlib/__init__.py index 76b708d..14c4491 100644 --- a/src/aspectlib/__init__.py +++ b/src/aspectlib/__init__.py @@ -40,7 +40,7 @@ __all__ = 'weave', 'Aspect', 'Proceed', 'Return', 'ALL_METHODS', 'NORMAL_METHODS' -__version__ = '1.3.0' +__version__ = '1.3.1' logger = getLogger(__name__) logdebug = logf(logger.debug) From 3167ed2b091a3dbdae9107adc94fec4191017c74 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ionel=20Cristian=20M=C4=83rie=C8=99?= Date: Sat, 12 Sep 2015 14:41:50 +0300 Subject: [PATCH 113/179] Remove project file. --- python-aspectlib.komodoproject | 24 ------------------------ 1 file changed, 24 deletions(-) delete mode 100644 python-aspectlib.komodoproject diff --git a/python-aspectlib.komodoproject b/python-aspectlib.komodoproject deleted file mode 100644 index 9a00932..0000000 --- a/python-aspectlib.komodoproject +++ /dev/null @@ -1,24 +0,0 @@ - - - - - 1 - - *.*~;*.bak;*.tmp;CVS;.#*;*.pyo;*.pyc;.svn;_svn;.git;.hg;.bzr;*%25*;tmp*.html;.DS_Store;*.swp;*.kpf;*.komodoproject;.komodotools;__pycache__;.tox;.ve;build;dist;*.egg-info;htmlcov; - New test plan #1 - 1 - 1 - 1 - .tox/3.4-nocover/bin/python3.4 - src - .tox/2.7-nocover/bin/python2.7 - src - - - - tests - Python3 - unittest - - - - From 004cae8abae113a73cda349719861ffa60f5ecbb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ionel=20Cristian=20M=C4=83rie=C8=99?= Date: Tue, 22 Sep 2015 07:04:59 +0300 Subject: [PATCH 114/179] Fix another tricky bug in the generator advising wrappers. If advising function use just "yield Proceed" instead of "yield Return(yield Proceed)" then the result value would be discarded. This has been fixed and now you can have advising functions with just "yield Proceed". --- src/aspectlib/__init__.py | 2 +- tests/test_aspectlib.py | 21 +++++++++++++++++++++ 2 files changed, 22 insertions(+), 1 deletion(-) diff --git a/src/aspectlib/__init__.py b/src/aspectlib/__init__.py index 14c4491..0ae61f2 100644 --- a/src/aspectlib/__init__.py +++ b/src/aspectlib/__init__.py @@ -231,7 +231,7 @@ def advising_generator_wrapper(*args, **kwargs): try: advice = advisor.send(result) except StopIteration: - return + raise StopIteration(result) finally: gen.close() elif advice is Return: diff --git a/tests/test_aspectlib.py b/tests/test_aspectlib.py index 195f93d..15510a4 100644 --- a/tests/test_aspectlib.py +++ b/tests/test_aspectlib.py @@ -1624,3 +1624,24 @@ def func(a): assert gen.next() is None result = pytest.raises(StopIteration, gen.next) assert result.value.args == (None,) + + +def test_aspect_chain_on_generator_no_return_advice(): + @aspectlib.Aspect + def foo(arg): + yield aspectlib.Proceed(arg + 1) + + @foo + @foo + @foo + def func(a): + assert a == 3 + raise StopIteration(a) + yield + + gen = func(0) + if hasattr(gen, '__next__'): + result = pytest.raises(StopIteration, gen.__next__) + else: + result = pytest.raises(StopIteration, gen.next) + assert result.value.args == (3,) From 87fe44fd43f6cb8336d534b467a7dad471755274 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ionel=20Cristian=20M=C4=83rie=C8=99?= Date: Tue, 22 Sep 2015 07:13:56 +0300 Subject: [PATCH 115/179] Port the fix in 004cae8 to Py3. --- src/aspectlib/py3support.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/aspectlib/py3support.py b/src/aspectlib/py3support.py index a0e453e..126abf3 100644 --- a/src/aspectlib/py3support.py +++ b/src/aspectlib/py3support.py @@ -43,7 +43,7 @@ def advising_generator_wrapper_py3(*args, **kwargs): try: advice = advisor.send(result) except StopIteration: - return + return result finally: gen.close() elif advice is Return: From e2da9ac466fcda7b41ed2a743286c7d1a86a2f14 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ionel=20Cristian=20M=C4=83rie=C8=99?= Date: Tue, 22 Sep 2015 07:16:50 +0300 Subject: [PATCH 116/179] Update changelog. --- CHANGELOG.rst | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/CHANGELOG.rst b/CHANGELOG.rst index 430db93..cd83854 100644 --- a/CHANGELOG.rst +++ b/CHANGELOG.rst @@ -1,6 +1,11 @@ Changelog ========= +1.3.2 (2015-09-22) +------------------ + +* Fixed another tricky bug in the generator advising wrappers - result was not returned if only `Proceed` was yielded. + 1.3.1 (2015-09-12) ------------------ From a46866eefa442e7c67dd98bb203d4c701a20e087 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ionel=20Cristian=20M=C4=83rie=C8=99?= Date: Tue, 22 Sep 2015 07:16:57 +0300 Subject: [PATCH 117/179] =?UTF-8?q?Bump=20version:=201.3.1=20=E2=86=92=201?= =?UTF-8?q?.3.2?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .bumpversion.cfg | 2 +- docs/conf.py | 2 +- setup.py | 2 +- src/aspectlib/__init__.py | 2 +- 4 files changed, 4 insertions(+), 4 deletions(-) diff --git a/.bumpversion.cfg b/.bumpversion.cfg index 8f0eba2..633b7d5 100644 --- a/.bumpversion.cfg +++ b/.bumpversion.cfg @@ -1,5 +1,5 @@ [bumpversion] -current_version = 1.3.1 +current_version = 1.3.2 commit = True tag = True diff --git a/docs/conf.py b/docs/conf.py index 57f1dee..b511254 100644 --- a/docs/conf.py +++ b/docs/conf.py @@ -24,7 +24,7 @@ year = '2014-2015' author = 'Ionel Cristian Mărieș' copyright = '{0}, {1}'.format(year, author) -version = release = '1.3.1' +version = release = '1.3.2' import sphinx_py3doc_enhanced_theme html_theme = "sphinx_py3doc_enhanced_theme" html_theme_path = [sphinx_py3doc_enhanced_theme.get_html_theme_path()] diff --git a/setup.py b/setup.py index 70b59ff..237e08e 100644 --- a/setup.py +++ b/setup.py @@ -23,7 +23,7 @@ def read(*names, **kwargs): setup( name='aspectlib', - version='1.3.1', + version='1.3.2', license='BSD', description="aspectlib is an aspect-oriented programming, monkey-patch and decorators library. It is useful when " "changing behavior in existing code is desired. It includes tools for debugging and testing: simple " diff --git a/src/aspectlib/__init__.py b/src/aspectlib/__init__.py index 0ae61f2..dab0135 100644 --- a/src/aspectlib/__init__.py +++ b/src/aspectlib/__init__.py @@ -40,7 +40,7 @@ __all__ = 'weave', 'Aspect', 'Proceed', 'Return', 'ALL_METHODS', 'NORMAL_METHODS' -__version__ = '1.3.1' +__version__ = '1.3.2' logger = getLogger(__name__) logdebug = logf(logger.debug) From 0d115700f984f946d2d927faa32e089c844e47dc Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ionel=20Cristian=20M=C4=83rie=C8=99?= Date: Fri, 2 Oct 2015 21:44:25 +0300 Subject: [PATCH 118/179] Fix spelling for ABSOLUTELY_ALL_METHODS. --- CHANGELOG.rst | 6 ++++++ src/aspectlib/__init__.py | 3 ++- 2 files changed, 8 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.rst b/CHANGELOG.rst index cd83854..2407dda 100644 --- a/CHANGELOG.rst +++ b/CHANGELOG.rst @@ -1,6 +1,12 @@ Changelog ========= +1.3.3 (2015-10-02) +------------------ + +* Fixed typo in ``ABSOLUTELLY_ALL_METHODS`` name (now ``ABSOLUTELY_ALL_METHODS``). Old name is still there for + backwards compatibility. + 1.3.2 (2015-09-22) ------------------ diff --git a/src/aspectlib/__init__.py b/src/aspectlib/__init__.py index dab0135..2766ed8 100644 --- a/src/aspectlib/__init__.py +++ b/src/aspectlib/__init__.py @@ -39,7 +39,7 @@ ClassType = type -__all__ = 'weave', 'Aspect', 'Proceed', 'Return', 'ALL_METHODS', 'NORMAL_METHODS' +__all__ = 'weave', 'Aspect', 'Proceed', 'Return', 'ALL_METHODS', 'NORMAL_METHODS', 'ABSOLUTELY_ALL_METHODS' __version__ = '1.3.2' logger = getLogger(__name__) @@ -48,6 +48,7 @@ UNSPECIFIED = Sentinel('UNSPECIFIED') ABSOLUTELLY_ALL_METHODS = re.compile('.*') +ABSOLUTELY_ALL_METHODS = ABSOLUTELLY_ALL_METHODS ALL_METHODS = re.compile('(?!__getattribute__$)') NORMAL_METHODS = re.compile('(?!__.*__$)') VALID_IDENTIFIER = re.compile(r'^[^\W\d]\w*$', re.UNICODE if PY3 else 0) From 040f40ce2e75a848ebac91a7e50714532ad3098e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ionel=20Cristian=20M=C4=83rie=C8=99?= Date: Fri, 2 Oct 2015 21:44:50 +0300 Subject: [PATCH 119/179] =?UTF-8?q?Bump=20version:=201.3.2=20=E2=86=92=201?= =?UTF-8?q?.3.3?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .bumpversion.cfg | 2 +- docs/conf.py | 2 +- setup.py | 2 +- src/aspectlib/__init__.py | 2 +- 4 files changed, 4 insertions(+), 4 deletions(-) diff --git a/.bumpversion.cfg b/.bumpversion.cfg index 633b7d5..694d024 100644 --- a/.bumpversion.cfg +++ b/.bumpversion.cfg @@ -1,5 +1,5 @@ [bumpversion] -current_version = 1.3.2 +current_version = 1.3.3 commit = True tag = True diff --git a/docs/conf.py b/docs/conf.py index b511254..cbbf7f9 100644 --- a/docs/conf.py +++ b/docs/conf.py @@ -24,7 +24,7 @@ year = '2014-2015' author = 'Ionel Cristian Mărieș' copyright = '{0}, {1}'.format(year, author) -version = release = '1.3.2' +version = release = '1.3.3' import sphinx_py3doc_enhanced_theme html_theme = "sphinx_py3doc_enhanced_theme" html_theme_path = [sphinx_py3doc_enhanced_theme.get_html_theme_path()] diff --git a/setup.py b/setup.py index 237e08e..083a135 100644 --- a/setup.py +++ b/setup.py @@ -23,7 +23,7 @@ def read(*names, **kwargs): setup( name='aspectlib', - version='1.3.2', + version='1.3.3', license='BSD', description="aspectlib is an aspect-oriented programming, monkey-patch and decorators library. It is useful when " "changing behavior in existing code is desired. It includes tools for debugging and testing: simple " diff --git a/src/aspectlib/__init__.py b/src/aspectlib/__init__.py index 2766ed8..6a447e7 100644 --- a/src/aspectlib/__init__.py +++ b/src/aspectlib/__init__.py @@ -40,7 +40,7 @@ __all__ = 'weave', 'Aspect', 'Proceed', 'Return', 'ALL_METHODS', 'NORMAL_METHODS', 'ABSOLUTELY_ALL_METHODS' -__version__ = '1.3.2' +__version__ = '1.3.3' logger = getLogger(__name__) logdebug = logf(logger.debug) From 505c3670440bfc1c11aa261de7743e8ab805b0b7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ionel=20Cristian=20M=C4=83rie=C8=99?= Date: Wed, 28 Oct 2015 23:33:20 +0200 Subject: [PATCH 120/179] Update examples. Ref #3. --- docs/examples.rst | 84 +++++++++++++++++++++++++++++++---------------- 1 file changed, 56 insertions(+), 28 deletions(-) diff --git a/docs/examples.rst b/docs/examples.rst index 086e921..1bf3d94 100644 --- a/docs/examples.rst +++ b/docs/examples.rst @@ -1,11 +1,9 @@ Examples ======== -Retry decorator +Retrying --------------- -TODO: Make a more configurable retry decorator and add it in ``aspectlib.contrib``. - .. code-block:: python class Client(object): @@ -17,45 +15,23 @@ TODO: Make a more configurable retry decorator and add it in ``aspectlib.contrib def action(self, data): # do some stuff - def retry(retries=(1, 5, 15, 30, 60), retry_on=(IOError, OSError), prepare=None): - assert len(retries) - - @aspectlib.Aspect - def retry_aspect(*args, **kwargs): - durations = retries - while True: - try: - yield aspectlib.Proceed - break - except retry_on as exc: - if durations: - logging.warn(exc) - time.sleep(durations[0]) - durations = durations[1:] - if prepare: - prepare(*args, **kwargs) - else: - raise - - return retry_aspect - Now patch the ``Client`` class to have the retry functionality on all its methods: .. code-block:: python - aspectlib.weave(Client, retry()) + aspectlib.weave(Client, aspectlib.contrib.retry()) or with different retry options (reconnect before retry): .. code-block:: python - aspectlib.weave(Client, retry(prepare=lambda self, *_: self.connect()) + aspectlib.weave(Client, aspectlib.contrib.retry(prepare=lambda self, *_: self.connect()) or just for one method: .. code-block:: python - aspectlib.weave(Client.action, retry()) + aspectlib.weave(Client.action, aspectlib.contrib.retry()) You can see here the advantage of having reusable retry functionality. Also, the retry handling is decoupled from the ``Client`` class. @@ -116,3 +92,55 @@ Mock behavior for tests: with aspectlib.weave(foo.Bar.stuff, mock_stuff): obj = foo.Bar() self.assertEqual(obj.stuff('special'), 'mocked-result') + +Profiling +--------- + +There's no decorator for such in aspectlib but you can use any of the many choices on `PyPI `_. + +Here's one example with `profilestats `_: + +.. code-block:: pycon + + >>> import os, sys, aspectlib, profilestats + >>> with aspectlib.weave('os.path.join', profilestats.profile(print_stats=10, dump_stats=True)): + ... print("os.path.join will be run with a profiler:") + ... os.path.join('a', 'b') + ... + os.path.join will be run with a profiler: + ... function calls in ... seconds + ... + Ordered by: cumulative time + ... + ncalls tottime percall cumtime percall filename:lineno(function) + 1 0.000 0.000 0.000 0.000 ... + 1 0.000 0.000 0.000 0.000 ... + 1 0.000 0.000 0.000 0.000 ... + 1 0.000 0.000 0.000 0.000 ... + ... + ... + 'a/b' + +You can even mix it with the :obj:`aspectlib.debug.log` aspect: + +.. code-block:: pycon + + >>> import aspectlib.debug + >>> with aspectlib.weave('os.path.join', [profilestats.profile(print_stats=10, dump_stats=True), aspectlib.debug.log(print_to=sys.stdout)]): + ... print("os.path.join will be run with a profiler and aspectlib.debug.log:") + ... os.path.join('a', 'b') + ... + os.path.join will be run with a profiler and aspectlib.debug.log: + join('a', 'b') <<< ... + ... function calls in ... seconds + ... + Ordered by: cumulative time + ... + ncalls tottime percall cumtime percall filename:lineno(function) + ... 0.000 0.000 0.000 0.000 ... + ... 0.000 0.000 0.000 0.000 ... + ... 0.000 0.000 0.000 0.000 ... + ... 0.000 0.000 0.000 0.000 ... + ... + ... + 'a/b' From 025df5143b96c8c86133c56c5677fc2b937e0186 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ionel=20Cristian=20M=C4=83rie=C8=99?= Date: Wed, 28 Oct 2015 23:35:13 +0200 Subject: [PATCH 121/179] Update test config. --- .travis.yml | 4 ---- ci/templates/tox.ini | 1 + setup.cfg | 2 +- tox.ini | 47 +------------------------------------------- 4 files changed, 3 insertions(+), 51 deletions(-) diff --git a/.travis.yml b/.travis.yml index 6ec991d..a7a6866 100644 --- a/.travis.yml +++ b/.travis.yml @@ -6,10 +6,6 @@ env: LD_PRELOAD=/lib/x86_64-linux-gnu/libSegFault.so matrix: - TOXENV=check - - TOXENV=2.6,coveralls,codecov - - TOXENV=2.6-debug,coveralls,codecov - - TOXENV=2.6-nocover - - TOXENV=2.6-nocover-debug - TOXENV=2.7,coveralls,codecov - TOXENV=2.7-debug,coveralls,codecov - TOXENV=2.7-nocover diff --git a/ci/templates/tox.ini b/ci/templates/tox.ini index 0439819..0c65548 100644 --- a/ci/templates/tox.ini +++ b/ci/templates/tox.ini @@ -21,6 +21,7 @@ deps = nose mock tornado + profilestats commands = {posargs:py.test -vv --ignore=src} diff --git a/setup.cfg b/setup.cfg index f1388ad..baa5fa3 100644 --- a/setup.cfg +++ b/setup.cfg @@ -68,7 +68,7 @@ python_versions = pypy dependencies = - : trollius !python_versions[3.*] + : trollius !python_versions[3.*] !python_versions[2.6] : asyncio &python_versions[3.3] : &python_versions[3.4] diff --git a/tox.ini b/tox.ini index 2b776a3..56b325d 100644 --- a/tox.ini +++ b/tox.ini @@ -2,10 +2,6 @@ envlist = clean, check, - 2.6, - 2.6-debug, - 2.6-nocover, - 2.6-nocover-debug, 2.7, 2.7-debug, 2.7-nocover, @@ -38,6 +34,7 @@ deps = nose mock tornado + profilestats commands = {posargs:py.test -vv --ignore=src} @@ -123,48 +120,6 @@ skip_install = true usedevelop = false deps = coverage -[testenv:2.6] -basepython = {env:TOXPYTHON:python2.6} -setenv = - {[testenv]setenv} - WITH_COVERAGE=yes -usedevelop = true -commands = - {posargs:py.test --cov --cov-report=term-missing -vv} -deps = - {[testenv]deps} - pytest-cov - trollius - -[testenv:2.6-debug] -basepython = {env:TOXPYTHON:python2.6} -setenv = - {[testenv]setenv} - ASPECTLIB_DEBUG=yes - WITH_COVERAGE=yes -usedevelop = true -commands = - {posargs:py.test --cov --cov-report=term-missing -vv} -deps = - {[testenv]deps} - pytest-cov - trollius - -[testenv:2.6-nocover] -basepython = {env:TOXPYTHON:python2.6} -deps = - {[testenv]deps} - trollius - -[testenv:2.6-nocover-debug] -basepython = {env:TOXPYTHON:python2.6} -setenv = - {[testenv]setenv} - ASPECTLIB_DEBUG=yes -deps = - {[testenv]deps} - trollius - [testenv:2.7] basepython = {env:TOXPYTHON:python2.7} setenv = From 97c8c158f712edb5adc8a210798611378eb4a025 Mon Sep 17 00:00:00 2001 From: Florian Bruhin Date: Mon, 30 Nov 2015 10:06:13 +0100 Subject: [PATCH 122/179] Fix typo in README --- README.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.rst b/README.rst index 6c560a4..cf49c4f 100644 --- a/README.rst +++ b/README.rst @@ -94,7 +94,7 @@ Pending: * *"Concerns"* (see `docs/todo.rst`) -If ``aspectlib.weave`` doesn't work for your scenario please report a bug ! +If ``aspectlib.weave`` doesn't work for your scenario please report a bug! Requirements ============ From 1a8dd39968970628a1aaa6a4229c2d0ef878435a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ionel=20Cristian=20M=C4=83rie=C8=99?= Date: Mon, 30 Nov 2015 05:21:33 +0200 Subject: [PATCH 123/179] Fix some spaces. --- docs/presentations/pycon.se-lightningtalk.rst | 2 +- src/aspectlib/debug.py | 6 +++--- src/aspectlib/test.py | 4 ++-- tests/mymod.py | 3 ++- 4 files changed, 8 insertions(+), 7 deletions(-) diff --git a/docs/presentations/pycon.se-lightningtalk.rst b/docs/presentations/pycon.se-lightningtalk.rst index 24534bd..3323684 100644 --- a/docs/presentations/pycon.se-lightningtalk.rst +++ b/docs/presentations/pycon.se-lightningtalk.rst @@ -48,7 +48,7 @@ What if you use it on a generator ? >>> t = raises(RuntimeError, lambda: list(broken_generator())) -Dooh ! No output. +Dooh! No output. How to fix it ? =============== diff --git a/src/aspectlib/debug.py b/src/aspectlib/debug.py index 2ee6225..c5c7303 100644 --- a/src/aspectlib/debug.py +++ b/src/aspectlib/debug.py @@ -110,7 +110,7 @@ def log(func=None, >>> try: ... a(weird=True) ... except Exception: - ... pass # naughty code ! + ... pass # naughty code! a(weird=True) <<< ... a ~ raised RuntimeError('BOOM!',) @@ -121,7 +121,7 @@ def log(func=None, ... try: ... float('invalid') ... except Exception as e: - ... pass # naughty code ! + ... pass # naughty code! float('invalid') <<< ... float ~ raised ValueError(...float...invalid...) @@ -132,7 +132,7 @@ def log(func=None, >>> try: ... log(call=False, result=False, print_to=sys.stdout)(float)('invalid') ... except Exception: - ... pass # naughty code ! + ... pass # naughty code! float('invalid') <<< ... float ~ raised ValueError(...float...invalid...) diff --git a/src/aspectlib/test.py b/src/aspectlib/test.py index b1a4606..e689efc 100644 --- a/src/aspectlib/test.py +++ b/src/aspectlib/test.py @@ -506,7 +506,7 @@ def replay(self, **options): ... print(mymod.func('some arg')) ... mymod.func('bogus arg') some result - Got bogus arg in the real code ! + Got bogus arg in the real code! STORY/REPLAY DIFF: --- expected... +++ actual... @@ -615,7 +615,7 @@ def unexpected(self): ... mymod.badfunc() ... except ValueError as exc: ... print(exc) - Got some arg in the real code ! + Got some arg in the real code! boom! >>> print(replay.unexpected) mymod.func('some arg') == None # returns diff --git a/tests/mymod.py b/tests/mymod.py index a5b9208..cc8a3d8 100644 --- a/tests/mymod.py +++ b/tests/mymod.py @@ -2,7 +2,8 @@ def func(arg): - print("Got", arg, "in the real code !") + print("Got", arg, "in the real code!") + def badfunc(): raise ValueError("boom!") From aa22001b44c3bc6a727cb59a6997eb5969f0e12c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ionel=20Cristian=20M=C4=83rie=C8=99?= Date: Tue, 1 Dec 2015 14:12:59 +0200 Subject: [PATCH 124/179] Make doctest pass on Windoze --- docs/examples.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/examples.rst b/docs/examples.rst index 1bf3d94..1ae4647 100644 --- a/docs/examples.rst +++ b/docs/examples.rst @@ -119,7 +119,7 @@ Here's one example with `profilestats Date: Tue, 1 Dec 2015 14:46:04 +0200 Subject: [PATCH 125/179] Argh ... --- docs/examples.rst | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/docs/examples.rst b/docs/examples.rst index 1ae4647..d07c2cf 100644 --- a/docs/examples.rst +++ b/docs/examples.rst @@ -113,10 +113,10 @@ Here's one example with `profilestats Date: Fri, 8 Apr 2016 16:46:08 +0300 Subject: [PATCH 126/179] Update url. --- AUTHORS.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/AUTHORS.rst b/AUTHORS.rst index 0e54887..d01607a 100644 --- a/AUTHORS.rst +++ b/AUTHORS.rst @@ -2,4 +2,4 @@ Authors ======= -* Ionel Cristian Mărieș - http://blog.ionelmc.ro +* Ionel Cristian Mărieș - https://blog.ionelmc.ro From 0842c53c3be280b2e803119d7ae6456b6317b546 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ionel=20Cristian=20M=C4=83rie=C8=99?= Date: Fri, 8 Apr 2016 16:46:53 +0300 Subject: [PATCH 127/179] Add a doctests env. --- docs/conf.py | 7 ++++--- docs/examples.rst | 2 +- docs/reference/aspectlib.debug.rst | 5 +++++ docs/reference/aspectlib.test.rst | 5 +++++ docs/requirements.txt | 1 - setup.py | 1 + src/aspectlib/debug.py | 2 +- tox.ini | 8 ++++++++ 8 files changed, 25 insertions(+), 6 deletions(-) diff --git a/docs/conf.py b/docs/conf.py index cbbf7f9..b048be2 100644 --- a/docs/conf.py +++ b/docs/conf.py @@ -3,15 +3,16 @@ import os - +import fields extensions = [ 'sphinx.ext.autodoc', 'sphinx.ext.autosummary', - 'sphinx.ext.todo', 'sphinx.ext.coverage', + 'sphinx.ext.doctest', 'sphinx.ext.ifconfig', + 'sphinx.ext.napoleon', + 'sphinx.ext.todo', 'sphinx.ext.viewcode', - 'sphinxcontrib.napoleon' ] if os.getenv('SPELLCHECK'): extensions += 'sphinxcontrib.spelling', diff --git a/docs/examples.rst b/docs/examples.rst index d07c2cf..038fa63 100644 --- a/docs/examples.rst +++ b/docs/examples.rst @@ -41,7 +41,7 @@ Debugging ... those damn sockets: -.. code-block:: pycon +.. doctest:: pycon >>> import aspectlib, socket, sys >>> with aspectlib.weave( diff --git a/docs/reference/aspectlib.debug.rst b/docs/reference/aspectlib.debug.rst index 6d9b0ad..862c373 100644 --- a/docs/reference/aspectlib.debug.rst +++ b/docs/reference/aspectlib.debug.rst @@ -1,6 +1,11 @@ Reference: ``aspectlib.debug`` ============================== +.. testsetup:: + + from aspectlib.debug import log + from aspectlib import weave + .. autosummary:: :nosignatures: diff --git a/docs/reference/aspectlib.test.rst b/docs/reference/aspectlib.test.rst index 7805641..2b5fc57 100644 --- a/docs/reference/aspectlib.test.rst +++ b/docs/reference/aspectlib.test.rst @@ -4,6 +4,11 @@ Reference: ``aspectlib.test`` This module aims to be a lightweight and flexible alternative to the popular `mock `_ framework and more. +.. testsetup:: + + from aspectlib.test import record, mock, Story + from aspectlib import weave + .. autosummary:: :nosignatures: diff --git a/docs/requirements.txt b/docs/requirements.txt index 1632a96..f53d26e 100644 --- a/docs/requirements.txt +++ b/docs/requirements.txt @@ -1,4 +1,3 @@ sphinx -sphinxcontrib-napoleon sphinx-py3doc-enhanced-theme -e . diff --git a/setup.py b/setup.py index 083a135..50e2eb6 100644 --- a/setup.py +++ b/setup.py @@ -61,6 +61,7 @@ def read(*names, **kwargs): 'recording', 'mocking', 'logger' ], install_requires=[ + 'fields' ], extras_require={ } diff --git a/src/aspectlib/debug.py b/src/aspectlib/debug.py index c5c7303..16b055e 100644 --- a/src/aspectlib/debug.py +++ b/src/aspectlib/debug.py @@ -98,7 +98,7 @@ def log(func=None, Returns: A decorator or a wrapper. - Example: + Example:: >>> @log(print_to=sys.stdout) ... def a(weird=False): diff --git a/tox.ini b/tox.ini index 56b325d..4eea1e4 100644 --- a/tox.ini +++ b/tox.ini @@ -57,6 +57,14 @@ commands = sphinx-build {posargs:-E} -b html docs dist/docs sphinx-build -b linkcheck docs dist/docs +[testenv:doctests] +deps = + sphinx + sphinx-py3doc-enhanced-theme + +commands = + sphinx-build {posargs:-E} -b doctest docs dist/docs + [testenv:bootstrap] deps = jinja2 From 98dcab17414bd736deec440f1139c7257f2531fe Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ionel=20Cristian=20M=C4=83rie=C8=99?= Date: Fri, 8 Apr 2016 16:55:11 +0300 Subject: [PATCH 128/179] Add 3.5 env. --- .travis.yml | 4 ++++ setup.cfg | 3 ++- tox.ini | 46 ++++++++++++++++++++++++++++++++++++++-------- 3 files changed, 44 insertions(+), 9 deletions(-) diff --git a/.travis.yml b/.travis.yml index a7a6866..378252e 100644 --- a/.travis.yml +++ b/.travis.yml @@ -18,6 +18,10 @@ env: - TOXENV=3.4-debug,coveralls,codecov - TOXENV=3.4-nocover - TOXENV=3.4-nocover-debug + - TOXENV=3.5,coveralls,codecov + - TOXENV=3.5-debug,coveralls,codecov + - TOXENV=3.5-nocover + - TOXENV=3.5-nocover-debug - TOXENV=pypy,coveralls,codecov - TOXENV=pypy-debug,coveralls,codecov - TOXENV=pypy-nocover diff --git a/setup.cfg b/setup.cfg index baa5fa3..18218ad 100644 --- a/setup.cfg +++ b/setup.cfg @@ -65,12 +65,13 @@ python_versions = 2.7 3.3 3.4 + 3.5 pypy dependencies = : trollius !python_versions[3.*] !python_versions[2.6] : asyncio &python_versions[3.3] - : &python_versions[3.4] + : !python_versions[2.*] !python_versions[3.3] !python_versions[pypy] coverage_flags = : true diff --git a/tox.ini b/tox.ini index 4eea1e4..7328b29 100644 --- a/tox.ini +++ b/tox.ini @@ -14,6 +14,10 @@ envlist = 3.4-debug, 3.4-nocover, 3.4-nocover-debug, + 3.5, + 3.5-debug, + 3.5-nocover, + 3.5-nocover-debug, pypy, pypy-debug, pypy-nocover, @@ -57,14 +61,6 @@ commands = sphinx-build {posargs:-E} -b html docs dist/docs sphinx-build -b linkcheck docs dist/docs -[testenv:doctests] -deps = - sphinx - sphinx-py3doc-enhanced-theme - -commands = - sphinx-build {posargs:-E} -b doctest docs dist/docs - [testenv:bootstrap] deps = jinja2 @@ -246,6 +242,40 @@ setenv = {[testenv]setenv} ASPECTLIB_DEBUG=yes +[testenv:3.5] +basepython = {env:TOXPYTHON:python3.5} +setenv = + {[testenv]setenv} + WITH_COVERAGE=yes +usedevelop = true +commands = + {posargs:py.test --cov --cov-report=term-missing -vv} +deps = + {[testenv]deps} + pytest-cov + +[testenv:3.5-debug] +basepython = {env:TOXPYTHON:python3.5} +setenv = + {[testenv]setenv} + ASPECTLIB_DEBUG=yes + WITH_COVERAGE=yes +usedevelop = true +commands = + {posargs:py.test --cov --cov-report=term-missing -vv} +deps = + {[testenv]deps} + pytest-cov + +[testenv:3.5-nocover] +basepython = {env:TOXPYTHON:python3.5} + +[testenv:3.5-nocover-debug] +basepython = {env:TOXPYTHON:python3.5} +setenv = + {[testenv]setenv} + ASPECTLIB_DEBUG=yes + [testenv:pypy] basepython = {env:TOXPYTHON:pypy} setenv = From 412049bfa3e26c2ce56e77471fb8e6dd89dcc119 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ionel=20Cristian=20M=C4=83rie=C8=99?= Date: Fri, 8 Apr 2016 16:55:57 +0300 Subject: [PATCH 129/179] Add classifier. --- setup.py | 1 + 1 file changed, 1 insertion(+) diff --git a/setup.py b/setup.py index 50e2eb6..0d0ed1e 100644 --- a/setup.py +++ b/setup.py @@ -51,6 +51,7 @@ def read(*names, **kwargs): 'Programming Language :: Python :: 3', 'Programming Language :: Python :: 3.3', 'Programming Language :: Python :: 3.4', + 'Programming Language :: Python :: 3.5', 'Programming Language :: Python :: Implementation :: CPython', 'Programming Language :: Python :: Implementation :: PyPy', 'Topic :: Utilities', From 78e743ad7fbe2b135904ca57112f3d0c33aa805c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ionel=20Cristian=20M=C4=83rie=C8=99?= Date: Fri, 8 Apr 2016 17:06:54 +0300 Subject: [PATCH 130/179] Correct weaving of methods, the weaved function should be unbound. --- CHANGELOG.rst | 6 ++++++ src/aspectlib/__init__.py | 4 ++-- tests/test_aspectlib.py | 42 +++++++++++++++++++++++++++++++++++++++ 3 files changed, 50 insertions(+), 2 deletions(-) diff --git a/CHANGELOG.rst b/CHANGELOG.rst index 2407dda..33ee339 100644 --- a/CHANGELOG.rst +++ b/CHANGELOG.rst @@ -1,6 +1,12 @@ Changelog ========= + +1.3.4 (2016-04-08) +------------------ + +* Corrected weaving of methods, the weaved function should be unbound. + 1.3.3 (2015-10-02) ------------------ diff --git a/src/aspectlib/__init__.py b/src/aspectlib/__init__.py index 6a447e7..3668c7d 100644 --- a/src/aspectlib/__init__.py +++ b/src/aspectlib/__init__.py @@ -464,7 +464,7 @@ def weave(target, aspects, **options): inst = target.__self__ name = target.__name__ logdebug("@ patching %r (%s) as instance method.", target, name) - func = getattr(inst, name) + func = target.__func__ setattr(inst, name, _checked_apply(aspects, func).__get__(inst, type(inst))) return Rollback(lambda: delattr(inst, name)) elif PY3 and isfunction(target): @@ -489,7 +489,7 @@ def weave(target, aspects, **options): inst = target.im_self name = target.__name__ logdebug("@ patching %r (%s) as instance method.", target, name) - func = getattr(inst, name) + func = target.im_func setattr(inst, name, _checked_apply(aspects, func).__get__(inst, type(inst))) return Rollback(lambda: delattr(inst, name)) else: diff --git a/tests/test_aspectlib.py b/tests/test_aspectlib.py index 15510a4..d55c9c2 100644 --- a/tests/test_aspectlib.py +++ b/tests/test_aspectlib.py @@ -1645,3 +1645,45 @@ def func(a): else: result = pytest.raises(StopIteration, gen.next) assert result.value.args == (3,) + + +def test_weave_method(): + calls = [] + intercepted = [] + + @aspectlib.Aspect(bind=True) + def intercept(func, *args): + intercepted.append(args) + yield aspectlib.Proceed(*args) + + class Foo(object): + def foo(self, arg): + calls.append((self, arg)) + + f = Foo() + with aspectlib.weave(f.foo, intercept): + f.foo(1) + + assert calls == [(f, 1)] + assert intercepted == [(f, 1)] + + +def test_weave_method_noargs(): + calls = [] + intercepted = [] + + @aspectlib.Aspect(bind=True) + def intercept(func, *args): + intercepted.append(args) + yield + + class Foo(object): + def foo(self, arg): + calls.append((self, arg)) + + f = Foo() + with aspectlib.weave(f.foo, intercept): + f.foo(1) + + assert calls == [(f, 1)] + assert intercepted == [(f, 1)] From 8f63d12a4a4eb039f483dbf502c24173ea21c1a2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ionel=20Cristian=20M=C4=83rie=C8=99?= Date: Sat, 9 Apr 2016 17:07:44 +0300 Subject: [PATCH 131/179] Add a weave fixture. --- CHANGELOG.rst | 5 +++-- setup.py | 5 +++++ src/aspectlib/__init__.py | 1 + src/aspectlib/pytestsupport.py | 12 ++++++++++++ tests/test_pytestsupport.py | 20 ++++++++++++++++++++ 5 files changed, 41 insertions(+), 2 deletions(-) create mode 100644 src/aspectlib/pytestsupport.py create mode 100644 tests/test_pytestsupport.py diff --git a/CHANGELOG.rst b/CHANGELOG.rst index 33ee339..5f83012 100644 --- a/CHANGELOG.rst +++ b/CHANGELOG.rst @@ -1,11 +1,12 @@ Changelog ========= - -1.3.4 (2016-04-08) +1.4.0 (2016-04-09) ------------------ * Corrected weaving of methods, the weaved function should be unbound. +* Rolling back only applies undos once. +* Added a convenience `weave` fixture for pytest. 1.3.3 (2015-10-02) ------------------ diff --git a/setup.py b/setup.py index 0d0ed1e..d2fdae1 100644 --- a/setup.py +++ b/setup.py @@ -65,5 +65,10 @@ def read(*names, **kwargs): 'fields' ], extras_require={ + }, + entry_points={ + 'pytest11': [ + 'aspectlib = aspectlib.pytestsupport' + ], } ) diff --git a/src/aspectlib/__init__.py b/src/aspectlib/__init__.py index 3668c7d..ae94641 100644 --- a/src/aspectlib/__init__.py +++ b/src/aspectlib/__init__.py @@ -307,6 +307,7 @@ def __enter__(self): def __exit__(self, *_): for rollback in self._rollbacks: rollback() + self._rollbacks.clear() rollback = __call__ = __exit__ diff --git a/src/aspectlib/pytestsupport.py b/src/aspectlib/pytestsupport.py new file mode 100644 index 0000000..cbf5dfc --- /dev/null +++ b/src/aspectlib/pytestsupport.py @@ -0,0 +1,12 @@ +import pytest +import aspectlib + + +@pytest.fixture +def weave(request): + def autocleaned_weave(*args, **kwargs): + entanglement = aspectlib.weave(*args, **kwargs) + request.addfinalizer(entanglement.rollback) + return entanglement + + return autocleaned_weave diff --git a/tests/test_pytestsupport.py b/tests/test_pytestsupport.py new file mode 100644 index 0000000..c1bb6ad --- /dev/null +++ b/tests/test_pytestsupport.py @@ -0,0 +1,20 @@ +from aspectlib import test + + +class Foo(object): + def bar(self): + return 1 + + +def test_fixture_1(weave): + weave(Foo.bar, test.mock(2)) + assert Foo().bar() == 2 + + +def test_fixture_2(weave): + assert Foo().bar() == 1 + + with weave(Foo.bar, test.mock(2)): + assert Foo().bar() == 2 + + assert Foo().bar() == 1 From f2e4f02d0d9f5c2490c35e502cf565f7c5577605 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ionel=20Cristian=20M=C4=83rie=C8=99?= Date: Sat, 9 Apr 2016 18:42:32 +0300 Subject: [PATCH 132/179] Update skel. --- .cookiecutterrc | 67 +++--- .coveragerc | 5 +- .editorconfig | 13 ++ .gitignore | 1 + .travis.yml | 13 +- CONTRIBUTING.rst | 15 +- LICENSE | 2 +- MANIFEST.in | 2 +- README.rst | 20 +- appveyor.yml | 383 ++++++++++++++++++++-------------- ci/appveyor-bootstrap.ps1 | 89 -------- ci/appveyor-bootstrap.py | 113 ++++++++++ ci/appveyor-with-compiler.cmd | 37 ++-- ci/bootstrap.py | 4 + ci/templates/.travis.yml | 18 +- ci/templates/appveyor.yml | 66 +++--- ci/templates/tox.ini | 21 +- docs/conf.py | 18 +- setup.cfg | 5 +- setup.py | 15 +- tox.ini | 21 +- 21 files changed, 547 insertions(+), 381 deletions(-) create mode 100644 .editorconfig delete mode 100644 ci/appveyor-bootstrap.ps1 create mode 100644 ci/appveyor-bootstrap.py diff --git a/.cookiecutterrc b/.cookiecutterrc index e1a90d3..962e811 100644 --- a/.cookiecutterrc +++ b/.cookiecutterrc @@ -1,34 +1,35 @@ -# This file exists so you can easily regenerate your project. -# -# Unfortunatelly cookiecutter can't use this right away so -# you have to copy this file to ~/.cookiecutterrc +# Generated by cookiepatcher, a small shim around cookiecutter (pip install cookiepatcher) -default_context: - - appveyor: 'yes' - c_extension_optional: 'no' - c_extension_support: 'no' - codacy: 'yes' - codeclimate: 'yes' - codecov: 'yes' - command_line_interface: 'no' - coveralls: 'yes' - distribution_name: 'aspectlib' - email: 'contact@ionelmc.ro' - full_name: 'Ionel Cristian Mărieș' - github_username: 'ionelmc' - landscape: 'yes' - package_name: 'aspectlib' - project_name: 'Aspectlib' - project_short_description: '``aspectlib`` is an aspect-oriented programming, monkey-patch and decorators library. It is useful when changing' - release_date: '2015-06-06' - repo_name: 'python-aspectlib' - requiresio: 'yes' - scrutinizer: 'yes' - sphinx_theme: 'sphinx-py3doc-enhanced-theme' - test_matrix_configurator: 'yes' - test_runner: 'pytest' - travis: 'yes' - version: '1.3.0' - website: 'http://blog.ionelmc.ro' - year: '2014-2015' +cookiecutter: + appveyor: 'yes' + c_extension_cython: 'yes' + c_extension_optional: 'no' + c_extension_support: 'no' + codacy: 'yes' + codeclimate: 'yes' + codecov: 'yes' + command_line_interface: 'no' + command_line_interface_bin_name: aspectlib + coveralls: 'yes' + distribution_name: aspectlib + email: contact@ionelmc.ro + full_name: Ionel Cristian Mărieș + github_username: ionelmc + landscape: 'yes' + package_name: aspectlib + project_name: Aspectlib + project_short_description: '``aspectlib`` is an aspect-oriented programming, monkey-patch + and decorators library. It is useful when changing' + release_date: '2015-06-06' + repo_name: python-aspectlib + requiresio: 'yes' + scrutinizer: 'yes' + sphinx_doctest: 'yes' + sphinx_theme: sphinx-py3doc-enhanced-theme + test_matrix_configurator: 'yes' + test_matrix_separate_coverage: 'no' + test_runner: pytest + travis: 'yes' + version: 1.3.3 + website: http://blog.ionelmc.ro + year: 2014-2016 diff --git a/.coveragerc b/.coveragerc index e0e7f76..772866d 100644 --- a/.coveragerc +++ b/.coveragerc @@ -9,7 +9,4 @@ parallel = true [report] show_missing = true precision = 2 -omit = - *migrations* - *py2chainmap* - *py2ordereddict* +omit = *migrations* diff --git a/.editorconfig b/.editorconfig new file mode 100644 index 0000000..4000618 --- /dev/null +++ b/.editorconfig @@ -0,0 +1,13 @@ +# see http://editorconfig.org +root = true + +[*] +end_of_line = lf +trim_trailing_whitespace = true +insert_final_newline = true +indent_style = space +indent_size = 4 +charset = utf-8 + +[*.{bat,cmd,ps1}] +end_of_line = crlf diff --git a/.gitignore b/.gitignore index cae27ab..9e8fc16 100644 --- a/.gitignore +++ b/.gitignore @@ -59,4 +59,5 @@ docs/_build .cache .pytest .bootstrap +.appveyor.token *.bak diff --git a/.travis.yml b/.travis.yml index 378252e..10c545c 100644 --- a/.travis.yml +++ b/.travis.yml @@ -1,11 +1,14 @@ language: python -python: 2.7 +python: '3.5' sudo: false env: global: - LD_PRELOAD=/lib/x86_64-linux-gnu/libSegFault.so + - LD_PRELOAD=/lib/x86_64-linux-gnu/libSegFault.so + - SEGFAULT_SIGNALS=all matrix: - TOXENV=check + - TOXENV=docs + - TOXENV=2.7,coveralls,codecov - TOXENV=2.7-debug,coveralls,codecov - TOXENV=2.7-nocover @@ -41,8 +44,12 @@ script: after_failure: - more .tox/log/* | cat - more .tox/*/log/* | cat +before_cache: + - rm -rf $HOME/.cache/pip/log +cache: + directories: + - $HOME/.cache/pip notifications: email: on_success: never on_failure: always - diff --git a/CONTRIBUTING.rst b/CONTRIBUTING.rst index 723d2af..6c0e46c 100644 --- a/CONTRIBUTING.rst +++ b/CONTRIBUTING.rst @@ -30,14 +30,15 @@ If you are proposing a feature: * Explain in detail how it would work. * Keep the scope as narrow as possible, to make it easier to implement. -* Remember that this is a volunteer-driven project, and that contributions are welcome :) +* Remember that this is a volunteer-driven project, and that code contributions are welcome :) Development =========== To set up `python-aspectlib` for local development: -1. `Fork python-aspectlib on GitHub `_. +1. Fork `python-aspectlib `_ + (look for the "Fork" button). 2. Clone your fork locally:: git clone git@github.com:your_name_here/python-aspectlib.git @@ -68,15 +69,15 @@ If you need some code review or feedback while you're developing the code just m For merging, you should: 1. Include passing tests (run ``tox``) [1]_. -2. Update documentation when there's new API, functionality etc. +2. Update documentation when there's new API, functionality etc. 3. Add a note to ``CHANGELOG.rst`` about the changes. 4. Add yourself to ``AUTHORS.rst``. -.. [1] If you don't have all the necessary python versions available locally you can rely on Travis - it will +.. [1] If you don't have all the necessary python versions available locally you can rely on Travis - it will `run the tests `_ for each change you add in the pull request. - + It will be slower though ... - + Tips ---- @@ -86,4 +87,4 @@ To run a subset of tests:: To run all the test environments in *parallel* (you need to ``pip install detox``):: - detox \ No newline at end of file + detox diff --git a/LICENSE b/LICENSE index 0209986..9ac3fbe 100644 --- a/LICENSE +++ b/LICENSE @@ -1,4 +1,4 @@ -Copyright (c) 2014-2015, Ionel Cristian Mărieș +Copyright (c) 2014-2016, Ionel Cristian Mărieș All rights reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the diff --git a/MANIFEST.in b/MANIFEST.in index 1a3e36c..bd48001 100644 --- a/MANIFEST.in +++ b/MANIFEST.in @@ -7,8 +7,8 @@ graft tests include .bumpversion.cfg include .coveragerc include .cookiecutterrc +include .editorconfig include .isort.cfg -include .pylintrc include AUTHORS.rst include CHANGELOG.rst diff --git a/README.rst b/README.rst index cf49c4f..51eca7a 100644 --- a/README.rst +++ b/README.rst @@ -1,6 +1,8 @@ -========= -Aspectlib -========= +======== +Overview +======== + +.. start-badges .. list-table:: :stub-columns: 1 @@ -67,19 +69,29 @@ Aspectlib :target: https://pypi.python.org/pypi/aspectlib .. |supported-implementations| image:: https://img.shields.io/pypi/implementation/aspectlib.svg?style=flat - :alt: Supported imlementations + :alt: Supported implementations :target: https://pypi.python.org/pypi/aspectlib .. |scrutinizer| image:: https://img.shields.io/scrutinizer/g/ionelmc/python-aspectlib/master.svg?style=flat :alt: Scrutinizer Status :target: https://scrutinizer-ci.com/g/ionelmc/python-aspectlib/ + +.. end-badges + ``aspectlib`` is an aspect-oriented programming, monkey-patch and decorators library. It is useful when changing behavior in existing code is desired. It includes tools for debugging and testing: simple mock/record and a complete capture/replay framework. * Free software: BSD license +Installation +============ + +:: + + pip install aspectlib + Documentation ============= diff --git a/appveyor.yml b/appveyor.yml index f916898..8e6f61d 100644 --- a/appveyor.yml +++ b/appveyor.yml @@ -1,169 +1,238 @@ version: '{branch}-{build}' build: off +cache: + - '%LOCALAPPDATA%\pip\Cache' environment: global: - WITH_COMPILER: "cmd /E:ON /V:ON /C .\\ci\\appveyor-with-compiler.cmd" + WITH_COMPILER: 'cmd /E:ON /V:ON /C .\ci\appveyor-with-compiler.cmd' matrix: - TOXENV: check - PYTHON_HOME: "C:\\Python27" - PYTHON_VERSION: "2.7" - PYTHON_ARCH: "32" - - TOXENV: "2.7,codecov" - TOXPYTHON: "C:\\Python27\\python.exe" - WINDOWS_SDK_VERSION: "v7.0" - PYTHON_HOME: "C:\\Python27" - PYTHON_VERSION: "2.7" - PYTHON_ARCH: "32" - - TOXENV: "2.7,codecov" - TOXPYTHON: "C:\\Python27-x64\\python.exe" - WINDOWS_SDK_VERSION: "v7.0" - PYTHON_HOME: "C:\\Python27-x64" - PYTHON_VERSION: "2.7" - PYTHON_ARCH: "64" - - TOXENV: "2.7-debug,codecov" - TOXPYTHON: "C:\\Python27\\python.exe" - WINDOWS_SDK_VERSION: "v7.0" - PYTHON_HOME: "C:\\Python27" - PYTHON_VERSION: "2.7" - PYTHON_ARCH: "32" - - TOXENV: "2.7-debug,codecov" - TOXPYTHON: "C:\\Python27-x64\\python.exe" - WINDOWS_SDK_VERSION: "v7.0" - PYTHON_HOME: "C:\\Python27-x64" - PYTHON_VERSION: "2.7" - PYTHON_ARCH: "64" - - TOXENV: "2.7-nocover" - TOXPYTHON: "C:\\Python27\\python.exe" - WINDOWS_SDK_VERSION: "v7.0" - PYTHON_HOME: "C:\\Python27" - PYTHON_VERSION: "2.7" - PYTHON_ARCH: "32" - - TOXENV: "2.7-nocover" - TOXPYTHON: "C:\\Python27-x64\\python.exe" - WINDOWS_SDK_VERSION: "v7.0" - PYTHON_HOME: "C:\\Python27-x64" - PYTHON_VERSION: "2.7" - PYTHON_ARCH: "64" - - TOXENV: "2.7-nocover-debug" - TOXPYTHON: "C:\\Python27\\python.exe" - WINDOWS_SDK_VERSION: "v7.0" - PYTHON_HOME: "C:\\Python27" - PYTHON_VERSION: "2.7" - PYTHON_ARCH: "32" - - TOXENV: "2.7-nocover-debug" - TOXPYTHON: "C:\\Python27-x64\\python.exe" - WINDOWS_SDK_VERSION: "v7.0" - PYTHON_HOME: "C:\\Python27-x64" - PYTHON_VERSION: "2.7" - PYTHON_ARCH: "64" - - TOXENV: "3.3,codecov" - TOXPYTHON: "C:\\Python33\\python.exe" - WINDOWS_SDK_VERSION: "v7.1" - PYTHON_HOME: "C:\\Python33" - PYTHON_VERSION: "3.3" - PYTHON_ARCH: "32" - - TOXENV: "3.3,codecov" - TOXPYTHON: "C:\\Python33-x64\\python.exe" - WINDOWS_SDK_VERSION: "v7.1" - PYTHON_HOME: "C:\\Python33-x64" - PYTHON_VERSION: "3.3" - PYTHON_ARCH: "64" - - TOXENV: "3.3-debug,codecov" - TOXPYTHON: "C:\\Python33\\python.exe" - WINDOWS_SDK_VERSION: "v7.1" - PYTHON_HOME: "C:\\Python33" - PYTHON_VERSION: "3.3" - PYTHON_ARCH: "32" - - TOXENV: "3.3-debug,codecov" - TOXPYTHON: "C:\\Python33-x64\\python.exe" - WINDOWS_SDK_VERSION: "v7.1" - PYTHON_HOME: "C:\\Python33-x64" - PYTHON_VERSION: "3.3" - PYTHON_ARCH: "64" - - TOXENV: "3.3-nocover" - TOXPYTHON: "C:\\Python33\\python.exe" - WINDOWS_SDK_VERSION: "v7.1" - PYTHON_HOME: "C:\\Python33" - PYTHON_VERSION: "3.3" - PYTHON_ARCH: "32" - - TOXENV: "3.3-nocover" - TOXPYTHON: "C:\\Python33-x64\\python.exe" - WINDOWS_SDK_VERSION: "v7.1" - PYTHON_HOME: "C:\\Python33-x64" - PYTHON_VERSION: "3.3" - PYTHON_ARCH: "64" - - TOXENV: "3.3-nocover-debug" - TOXPYTHON: "C:\\Python33\\python.exe" - WINDOWS_SDK_VERSION: "v7.1" - PYTHON_HOME: "C:\\Python33" - PYTHON_VERSION: "3.3" - PYTHON_ARCH: "32" - - TOXENV: "3.3-nocover-debug" - TOXPYTHON: "C:\\Python33-x64\\python.exe" - WINDOWS_SDK_VERSION: "v7.1" - PYTHON_HOME: "C:\\Python33-x64" - PYTHON_VERSION: "3.3" - PYTHON_ARCH: "64" - - TOXENV: "3.4,codecov" - TOXPYTHON: "C:\\Python34\\python.exe" - WINDOWS_SDK_VERSION: "v7.1" - PYTHON_HOME: "C:\\Python34" - PYTHON_VERSION: "3.4" - PYTHON_ARCH: "32" - - TOXENV: "3.4,codecov" - TOXPYTHON: "C:\\Python34-x64\\python.exe" - WINDOWS_SDK_VERSION: "v7.1" - PYTHON_HOME: "C:\\Python34-x64" - PYTHON_VERSION: "3.4" - PYTHON_ARCH: "64" - - TOXENV: "3.4-debug,codecov" - TOXPYTHON: "C:\\Python34\\python.exe" - WINDOWS_SDK_VERSION: "v7.1" - PYTHON_HOME: "C:\\Python34" - PYTHON_VERSION: "3.4" - PYTHON_ARCH: "32" - - TOXENV: "3.4-debug,codecov" - TOXPYTHON: "C:\\Python34-x64\\python.exe" - WINDOWS_SDK_VERSION: "v7.1" - PYTHON_HOME: "C:\\Python34-x64" - PYTHON_VERSION: "3.4" - PYTHON_ARCH: "64" - - TOXENV: "3.4-nocover" - TOXPYTHON: "C:\\Python34\\python.exe" - WINDOWS_SDK_VERSION: "v7.1" - PYTHON_HOME: "C:\\Python34" - PYTHON_VERSION: "3.4" - PYTHON_ARCH: "32" - - TOXENV: "3.4-nocover" - TOXPYTHON: "C:\\Python34-x64\\python.exe" - WINDOWS_SDK_VERSION: "v7.1" - PYTHON_HOME: "C:\\Python34-x64" - PYTHON_VERSION: "3.4" - PYTHON_ARCH: "64" - - TOXENV: "3.4-nocover-debug" - TOXPYTHON: "C:\\Python34\\python.exe" - WINDOWS_SDK_VERSION: "v7.1" - PYTHON_HOME: "C:\\Python34" - PYTHON_VERSION: "3.4" - PYTHON_ARCH: "32" - - TOXENV: "3.4-nocover-debug" - TOXPYTHON: "C:\\Python34-x64\\python.exe" - WINDOWS_SDK_VERSION: "v7.1" - PYTHON_HOME: "C:\\Python34-x64" - PYTHON_VERSION: "3.4" - PYTHON_ARCH: "64" + PYTHON_HOME: C:\Python27 + PYTHON_VERSION: '2.7' + PYTHON_ARCH: '32' + + - TOXENV: '2.7,codecov' + TOXPYTHON: C:\Python27\python.exe + PYTHON_HOME: C:\Python27 + PYTHON_VERSION: '2.7' + PYTHON_ARCH: '32' + + - TOXENV: '2.7,codecov' + TOXPYTHON: C:\Python27-x64\python.exe + WINDOWS_SDK_VERSION: v7.0 + PYTHON_HOME: C:\Python27-x64 + PYTHON_VERSION: '2.7' + PYTHON_ARCH: '64' + + - TOXENV: '2.7-debug,codecov' + TOXPYTHON: C:\Python27\python.exe + PYTHON_HOME: C:\Python27 + PYTHON_VERSION: '2.7' + PYTHON_ARCH: '32' + + - TOXENV: '2.7-debug,codecov' + TOXPYTHON: C:\Python27-x64\python.exe + WINDOWS_SDK_VERSION: v7.0 + PYTHON_HOME: C:\Python27-x64 + PYTHON_VERSION: '2.7' + PYTHON_ARCH: '64' + + - TOXENV: '2.7-nocover' + TOXPYTHON: C:\Python27\python.exe + PYTHON_HOME: C:\Python27 + PYTHON_VERSION: '2.7' + PYTHON_ARCH: '32' + + - TOXENV: '2.7-nocover' + TOXPYTHON: C:\Python27-x64\python.exe + WINDOWS_SDK_VERSION: v7.0 + PYTHON_HOME: C:\Python27-x64 + PYTHON_VERSION: '2.7' + PYTHON_ARCH: '64' + + - TOXENV: '2.7-nocover-debug' + TOXPYTHON: C:\Python27\python.exe + PYTHON_HOME: C:\Python27 + PYTHON_VERSION: '2.7' + PYTHON_ARCH: '32' + + - TOXENV: '2.7-nocover-debug' + TOXPYTHON: C:\Python27-x64\python.exe + WINDOWS_SDK_VERSION: v7.0 + PYTHON_HOME: C:\Python27-x64 + PYTHON_VERSION: '2.7' + PYTHON_ARCH: '64' + + - TOXENV: '3.3,codecov' + TOXPYTHON: C:\Python33\python.exe + PYTHON_HOME: C:\Python33 + PYTHON_VERSION: '3.3' + PYTHON_ARCH: '32' + + - TOXENV: '3.3,codecov' + TOXPYTHON: C:\Python33-x64\python.exe + WINDOWS_SDK_VERSION: v7.1 + PYTHON_HOME: C:\Python33-x64 + PYTHON_VERSION: '3.3' + PYTHON_ARCH: '64' + + - TOXENV: '3.3-debug,codecov' + TOXPYTHON: C:\Python33\python.exe + PYTHON_HOME: C:\Python33 + PYTHON_VERSION: '3.3' + PYTHON_ARCH: '32' + + - TOXENV: '3.3-debug,codecov' + TOXPYTHON: C:\Python33-x64\python.exe + WINDOWS_SDK_VERSION: v7.1 + PYTHON_HOME: C:\Python33-x64 + PYTHON_VERSION: '3.3' + PYTHON_ARCH: '64' + + - TOXENV: '3.3-nocover' + TOXPYTHON: C:\Python33\python.exe + PYTHON_HOME: C:\Python33 + PYTHON_VERSION: '3.3' + PYTHON_ARCH: '32' + + - TOXENV: '3.3-nocover' + TOXPYTHON: C:\Python33-x64\python.exe + WINDOWS_SDK_VERSION: v7.1 + PYTHON_HOME: C:\Python33-x64 + PYTHON_VERSION: '3.3' + PYTHON_ARCH: '64' + + - TOXENV: '3.3-nocover-debug' + TOXPYTHON: C:\Python33\python.exe + PYTHON_HOME: C:\Python33 + PYTHON_VERSION: '3.3' + PYTHON_ARCH: '32' + + - TOXENV: '3.3-nocover-debug' + TOXPYTHON: C:\Python33-x64\python.exe + WINDOWS_SDK_VERSION: v7.1 + PYTHON_HOME: C:\Python33-x64 + PYTHON_VERSION: '3.3' + PYTHON_ARCH: '64' + + - TOXENV: '3.4,codecov' + TOXPYTHON: C:\Python34\python.exe + PYTHON_HOME: C:\Python34 + PYTHON_VERSION: '3.4' + PYTHON_ARCH: '32' + + - TOXENV: '3.4,codecov' + TOXPYTHON: C:\Python34-x64\python.exe + WINDOWS_SDK_VERSION: v7.1 + PYTHON_HOME: C:\Python34-x64 + PYTHON_VERSION: '3.4' + PYTHON_ARCH: '64' + + - TOXENV: '3.4-debug,codecov' + TOXPYTHON: C:\Python34\python.exe + PYTHON_HOME: C:\Python34 + PYTHON_VERSION: '3.4' + PYTHON_ARCH: '32' + + - TOXENV: '3.4-debug,codecov' + TOXPYTHON: C:\Python34-x64\python.exe + WINDOWS_SDK_VERSION: v7.1 + PYTHON_HOME: C:\Python34-x64 + PYTHON_VERSION: '3.4' + PYTHON_ARCH: '64' + + - TOXENV: '3.4-nocover' + TOXPYTHON: C:\Python34\python.exe + PYTHON_HOME: C:\Python34 + PYTHON_VERSION: '3.4' + PYTHON_ARCH: '32' + + - TOXENV: '3.4-nocover' + TOXPYTHON: C:\Python34-x64\python.exe + WINDOWS_SDK_VERSION: v7.1 + PYTHON_HOME: C:\Python34-x64 + PYTHON_VERSION: '3.4' + PYTHON_ARCH: '64' + + - TOXENV: '3.4-nocover-debug' + TOXPYTHON: C:\Python34\python.exe + PYTHON_HOME: C:\Python34 + PYTHON_VERSION: '3.4' + PYTHON_ARCH: '32' + + - TOXENV: '3.4-nocover-debug' + TOXPYTHON: C:\Python34-x64\python.exe + WINDOWS_SDK_VERSION: v7.1 + PYTHON_HOME: C:\Python34-x64 + PYTHON_VERSION: '3.4' + PYTHON_ARCH: '64' + + - TOXENV: '3.5,codecov' + TOXPYTHON: C:\Python35\python.exe + PYTHON_HOME: C:\Python35 + PYTHON_VERSION: '3.5' + PYTHON_ARCH: '32' + + - TOXENV: '3.5,codecov' + TOXPYTHON: C:\Python35-x64\python.exe + PYTHON_HOME: C:\Python35-x64 + PYTHON_VERSION: '3.5' + PYTHON_ARCH: '64' + + - TOXENV: '3.5-debug,codecov' + TOXPYTHON: C:\Python35\python.exe + PYTHON_HOME: C:\Python35 + PYTHON_VERSION: '3.5' + PYTHON_ARCH: '32' + + - TOXENV: '3.5-debug,codecov' + TOXPYTHON: C:\Python35-x64\python.exe + PYTHON_HOME: C:\Python35-x64 + PYTHON_VERSION: '3.5' + PYTHON_ARCH: '64' + + - TOXENV: '3.5-nocover' + TOXPYTHON: C:\Python35\python.exe + PYTHON_HOME: C:\Python35 + PYTHON_VERSION: '3.5' + PYTHON_ARCH: '32' + + - TOXENV: '3.5-nocover' + TOXPYTHON: C:\Python35-x64\python.exe + PYTHON_HOME: C:\Python35-x64 + PYTHON_VERSION: '3.5' + PYTHON_ARCH: '64' + + - TOXENV: '3.5-nocover-debug' + TOXPYTHON: C:\Python35\python.exe + PYTHON_HOME: C:\Python35 + PYTHON_VERSION: '3.5' + PYTHON_ARCH: '32' + + - TOXENV: '3.5-nocover-debug' + TOXPYTHON: C:\Python35-x64\python.exe + PYTHON_HOME: C:\Python35-x64 + PYTHON_VERSION: '3.5' + PYTHON_ARCH: '64' + init: - - "ECHO %TOXENV%" - - ps: "ls C:\\Python*" + - ps: echo $env:TOXENV + - ps: ls C:\Python* install: - - "powershell ci\\appveyor-bootstrap.ps1" + - python -u ci\appveyor-bootstrap.py + - '%PYTHON_HOME%\Scripts\virtualenv --version' + - '%PYTHON_HOME%\Scripts\easy_install --version' + - '%PYTHON_HOME%\Scripts\pip --version' + - '%PYTHON_HOME%\Scripts\tox --version' test_script: - - "%PYTHON_HOME%\\Scripts\\tox --version" - - "%PYTHON_HOME%\\Scripts\\virtualenv --version" - - "%PYTHON_HOME%\\Scripts\\pip --version" - - "%WITH_COMPILER% %PYTHON_HOME%\\Scripts\\tox" -after_test: - - "IF \"%TOXENV:~-8,8%\" == \"-nocover\" %WITH_COMPILER% %TOXPYTHON% setup.py bdist_wheel" + - '%WITH_COMPILER% %PYTHON_HOME%\Scripts\tox' + +on_failure: + - ps: dir "env:" + - ps: get-content .tox\*\log\* artifacts: - path: dist\* +### To enable remote debugging uncomment this (also, see: http://www.appveyor.com/docs/how-to/rdp-to-build-worker): +# on_finish: +# - ps: $blockRdp = $true; iex ((new-object net.webclient).DownloadString('https://raw.githubusercontent.com/appveyor/ci/master/scripts/enable-rdp.ps1')) diff --git a/ci/appveyor-bootstrap.ps1 b/ci/appveyor-bootstrap.ps1 deleted file mode 100644 index e3d0f30..0000000 --- a/ci/appveyor-bootstrap.ps1 +++ /dev/null @@ -1,89 +0,0 @@ -# Source: https://github.com/pypa/python-packaging-user-guide/blob/master/source/code/install.ps1 -# Sample script to install Python and pip under Windows -# Authors: Olivier Grisel and Kyle Kastner -# License: CC0 1.0 Universal: http://creativecommons.org/publicdomain/zero/1.0/ - -$BASE_URL = "https://www.python.org/ftp/python/" -$GET_PIP_URL = "https://bootstrap.pypa.io/get-pip.py" -$GET_PIP_PATH = "C:\get-pip.py" - - -function DownloadPython ($python_version, $platform_suffix) { - $webclient = New-Object System.Net.WebClient - $filename = "python-" + $python_version + $platform_suffix + ".msi" - $url = $BASE_URL + $python_version + "/" + $filename - - $basedir = $pwd.Path + "\" - $filepath = $basedir + $filename - if (Test-Path $filename) { - Write-Host "Reusing" $filepath - return $filepath - } - - # Download and retry up to 5 times in case of network transient errors. - Write-Host "Downloading" $filename "from" $url - $retry_attempts = 3 - for($i=0; $i -lt $retry_attempts; $i++){ - try { - $webclient.DownloadFile($url, $filepath) - break - } - Catch [Exception]{ - Start-Sleep 1 - } - } - Write-Host "File saved at" $filepath - return $filepath -} - - -function InstallPython ($python_version, $architecture, $python_home) { - Write-Host "Installing Python" $python_version "for" $architecture "bit architecture to" $python_home - if (Test-Path $python_home) { - Write-Host $python_home "already exists, skipping." - return $false - } - if ($architecture -eq "32") { - $platform_suffix = "" - } else { - $platform_suffix = ".amd64" - } - $filepath = DownloadPython $python_version $platform_suffix - Write-Host "Installing" $filepath "to" $python_home - $args = "/qn /i $filepath TARGETDIR=$python_home" - Write-Host "msiexec.exe" $args - Start-Process -FilePath "msiexec.exe" -ArgumentList $args -Wait -Passthru - Write-Host "Python $python_version ($architecture) installation complete" - return $true -} - - -function InstallPip ($python_home) { - $pip_path = $python_home + "/Scripts/pip.exe" - $python_path = $python_home + "/python.exe" - if (-not(Test-Path $pip_path)) { - Write-Host "Installing pip..." - $webclient = New-Object System.Net.WebClient - $webclient.DownloadFile($GET_PIP_URL, $GET_PIP_PATH) - Write-Host "Executing:" $python_path $GET_PIP_PATH - Start-Process -FilePath "$python_path" -ArgumentList "$GET_PIP_PATH" -Wait -Passthru - } else { - Write-Host "pip already installed." - } -} - -function InstallPackage ($python_home, $pkg) { - $pip_path = $python_home + "/Scripts/pip.exe" - & $pip_path install $pkg -} - -function main () { - InstallPython $env:PYTHON_VERSION $env:PYTHON_ARCH $env:PYTHON_HOME - InstallPip $env:PYTHON_HOME - InstallPackage $env:PYTHON_HOME "setuptools>=18.0.1" - InstallPackage $env:PYTHON_HOME wheel - InstallPackage $env:PYTHON_HOME tox - InstallPackage $env:PYTHON_HOME "virtualenv>=13.1.0" -} - -main diff --git a/ci/appveyor-bootstrap.py b/ci/appveyor-bootstrap.py new file mode 100644 index 0000000..74abd0b --- /dev/null +++ b/ci/appveyor-bootstrap.py @@ -0,0 +1,113 @@ +""" +AppVeyor will at least have few Pythons around so there's no point of implementing a bootstrapper in PowerShell. + +This is a port of https://github.com/pypa/python-packaging-user-guide/blob/master/source/code/install.ps1 +with various fixes and improvements that just weren't feasible to implement in PowerShell. +""" +from __future__ import print_function +from os import environ +from os.path import exists +from subprocess import check_call + +try: + from urllib.request import urlretrieve +except ImportError: + from urllib import urlretrieve + +BASE_URL = "https://www.python.org/ftp/python/" +GET_PIP_URL = "https://bootstrap.pypa.io/get-pip.py" +GET_PIP_PATH = "C:\get-pip.py" +URLS = { + ("2.7", "64"): BASE_URL + "2.7.10/python-2.7.10.amd64.msi", + ("2.7", "32"): BASE_URL + "2.7.10/python-2.7.10.msi", + # NOTE: no .msi installer for 3.3.6 + ("3.3", "64"): BASE_URL + "3.3.3/python-3.3.3.amd64.msi", + ("3.3", "32"): BASE_URL + "3.3.3/python-3.3.3.msi", + ("3.4", "64"): BASE_URL + "3.4.3/python-3.4.3.amd64.msi", + ("3.4", "32"): BASE_URL + "3.4.3/python-3.4.3.msi", + ("3.5", "64"): BASE_URL + "3.5.0/python-3.5.0-amd64.exe", + ("3.5", "32"): BASE_URL + "3.5.0/python-3.5.0.exe", +} +INSTALL_CMD = { + # Commands are allowed to fail only if they are not the last command. Eg: uninstall (/x) allowed to fail. + "2.7": [["msiexec.exe", "/L*+!", "install.log", "/qn", "/x", "{path}"], + ["msiexec.exe", "/L*+!", "install.log", "/qn", "/i", "{path}", "TARGETDIR={home}"]], + "3.3": [["msiexec.exe", "/L*+!", "install.log", "/qn", "/x", "{path}"], + ["msiexec.exe", "/L*+!", "install.log", "/qn", "/i", "{path}", "TARGETDIR={home}"]], + "3.4": [["msiexec.exe", "/L*+!", "install.log", "/qn", "/x", "{path}"], + ["msiexec.exe", "/L*+!", "install.log", "/qn", "/i", "{path}", "TARGETDIR={home}"]], + "3.5": [["{path}", "/quiet", "TargetDir={home}"]], +} + + +def download_file(url, path): + print("Downloading: {} (into {})".format(url, path)) + progress = [0, 0] + + def report(count, size, total): + progress[0] = count * size + if progress[0] - progress[1] > 1000000: + progress[1] = progress[0] + print("Downloaded {:,}/{:,} ...".format(progress[1], total)) + + dest, _ = urlretrieve(url, path, reporthook=report) + return dest + + +def install_python(version, arch, home): + print("Installing Python", version, "for", arch, "bit architecture to", home) + if exists(home): + return + + path = download_python(version, arch) + print("Installing", path, "to", home) + success = False + for cmd in INSTALL_CMD[version]: + cmd = [part.format(home=home, path=path) for part in cmd] + print("Running:", " ".join(cmd)) + try: + check_call(cmd) + except Exception as exc: + print("Failed command", cmd, "with:", exc) + if exists("install.log"): + with open("install.log") as fh: + print(fh.read()) + else: + success = True + if success: + print("Installation complete!") + else: + print("Installation failed") + + +def download_python(version, arch): + for _ in range(3): + try: + return download_file(URLS[version, arch], "installer.exe") + except Exception as exc: + print("Failed to download:", exc) + print("Retrying ...") + + +def install_pip(home): + pip_path = home + "/Scripts/pip.exe" + python_path = home + "/python.exe" + if exists(pip_path): + print("pip already installed.") + else: + print("Installing pip...") + download_file(GET_PIP_URL, GET_PIP_PATH) + print("Executing:", python_path, GET_PIP_PATH) + check_call([python_path, GET_PIP_PATH]) + + +def install_packages(home, *packages): + cmd = [home + "/Scripts/pip.exe", "install"] + cmd.extend(packages) + check_call(cmd) + + +if __name__ == "__main__": + install_python(environ['PYTHON_VERSION'], environ['PYTHON_ARCH'], environ['PYTHON_HOME']) + install_pip(environ['PYTHON_HOME']) + install_packages(environ['PYTHON_HOME'], "setuptools>=18.0.1", "wheel", "tox", "virtualenv>=13.1.0") diff --git a/ci/appveyor-with-compiler.cmd b/ci/appveyor-with-compiler.cmd index 3619733..7f82a02 100644 --- a/ci/appveyor-with-compiler.cmd +++ b/ci/appveyor-with-compiler.cmd @@ -17,21 +17,30 @@ :: :: Author: Olivier Grisel :: License: CC0 1.0 Universal: http://creativecommons.org/publicdomain/zero/1.0/ -@ECHO OFF - SET COMMAND_TO_RUN=%* SET WIN_SDK_ROOT=C:\Program Files\Microsoft SDKs\Windows +SET WIN_WDK="c:\Program Files (x86)\Windows Kits\10\Include\wdf" +ECHO SDK: %WINDOWS_SDK_VERSION% ARCH: %PYTHON_ARCH% + + +IF "%PYTHON_VERSION%"=="3.5" ( + IF EXIST %WIN_WDK% ( + REM See: https://connect.microsoft.com/VisualStudio/feedback/details/1610302/ + REN %WIN_WDK% 0wdf + ) + GOTO main +) -IF "%PYTHON_ARCH%"=="64" ( - ECHO SDK: %WINDOWS_SDK_VERSION% ARCH: %PYTHON_ARCH% - SET DISTUTILS_USE_SDK=1 - SET MSSdk=1 - "%WIN_SDK_ROOT%\%WINDOWS_SDK_VERSION%\Setup\WindowsSdkVer.exe" -q -version:%WINDOWS_SDK_VERSION% - "%WIN_SDK_ROOT%\%WINDOWS_SDK_VERSION%\Bin\SetEnv.cmd" /x64 /release - ECHO Executing: %COMMAND_TO_RUN% - call %COMMAND_TO_RUN% || EXIT 1 -) ELSE ( - ECHO SDK: %WINDOWS_SDK_VERSION% ARCH: %PYTHON_ARCH% - ECHO Executing: %COMMAND_TO_RUN% - call %COMMAND_TO_RUN% || EXIT 1 +IF "%PYTHON_ARCH%"=="32" ( + GOTO main ) + +SET DISTUTILS_USE_SDK=1 +SET MSSdk=1 +"%WIN_SDK_ROOT%\%WINDOWS_SDK_VERSION%\Setup\WindowsSdkVer.exe" -q -version:%WINDOWS_SDK_VERSION% +CALL "%WIN_SDK_ROOT%\%WINDOWS_SDK_VERSION%\Bin\SetEnv.cmd" /x64 /release + +:main + +ECHO Executing: %COMMAND_TO_RUN% +CALL %COMMAND_TO_RUN% || EXIT 1 diff --git a/ci/bootstrap.py b/ci/bootstrap.py index 466bb8f..fdfbaf6 100755 --- a/ci/bootstrap.py +++ b/ci/bootstrap.py @@ -31,14 +31,17 @@ exec(compile(open(activate, "rb").read(), activate, "exec"), dict(__file__=activate)) import jinja2 + import matrix + jinja = jinja2.Environment( loader=jinja2.FileSystemLoader(join(base_path, "ci", "templates")), trim_blocks=True, lstrip_blocks=True, keep_trailing_newline=True ) + tox_environments = {} for (alias, conf) in matrix.from_file(join(base_path, "setup.cfg")).items(): python = conf["python_versions"] @@ -57,6 +60,7 @@ if "environment_variables" in conf: tox_environments[alias].update(env_vars=env_vars.split()) + for name in os.listdir(join("ci", "templates")): with open(join(base_path, name), "w") as fh: fh.write(jinja.get_template(name).render(tox_environments=tox_environments)) diff --git a/ci/templates/.travis.yml b/ci/templates/.travis.yml index 34f68d5..a7f4187 100644 --- a/ci/templates/.travis.yml +++ b/ci/templates/.travis.yml @@ -1,15 +1,17 @@ language: python -python: 2.7 +python: '3.5' sudo: false env: global: - LD_PRELOAD=/lib/x86_64-linux-gnu/libSegFault.so + - LD_PRELOAD=/lib/x86_64-linux-gnu/libSegFault.so + - SEGFAULT_SIGNALS=all matrix: - TOXENV=check -{% for env, config in tox_environments|dictsort %} - - TOXENV={{ env }}{% if config.cover %},coveralls,codecov{% endif %} - + - TOXENV=docs +{% for env, config in tox_environments|dictsort %}{{ '' }} + - TOXENV={{ env }}{% if config.cover %},coveralls,codecov{% endif -%} {% endfor %} + before_install: - python --version - uname -a @@ -25,8 +27,12 @@ script: after_failure: - more .tox/log/* | cat - more .tox/*/log/* | cat +before_cache: + - rm -rf $HOME/.cache/pip/log +cache: + directories: + - $HOME/.cache/pip notifications: email: on_success: never on_failure: always - diff --git a/ci/templates/appveyor.yml b/ci/templates/appveyor.yml index 0d1ea53..4695f1a 100644 --- a/ci/templates/appveyor.yml +++ b/ci/templates/appveyor.yml @@ -1,39 +1,53 @@ version: '{branch}-{build}' build: off +cache: + - '%LOCALAPPDATA%\pip\Cache' environment: global: - WITH_COMPILER: "cmd /E:ON /V:ON /C .\\ci\\appveyor-with-compiler.cmd" + WITH_COMPILER: 'cmd /E:ON /V:ON /C .\ci\appveyor-with-compiler.cmd' matrix: - TOXENV: check - PYTHON_HOME: "C:\\Python27" - PYTHON_VERSION: "2.7" - PYTHON_ARCH: "32" -{% for env, config in tox_environments|dictsort %}{% if env.startswith('2.7') or env.startswith('3.4') or env.startswith('3.3') %} - - TOXENV: "{{ env }}{% if config.cover %},codecov{% endif %}" - TOXPYTHON: "C:\\Python{{ env[:3].replace('.', '') }}\\python.exe" - WINDOWS_SDK_VERSION: "v7.{{ '1' if env[0] == '3' else '0' }}" - PYTHON_HOME: "C:\\Python{{ env[:3].replace('.', '') }}" - PYTHON_VERSION: "{{ env[:3] }}" - PYTHON_ARCH: "32" - - TOXENV: "{{ env }}{% if config.cover %},codecov{% endif %}" - TOXPYTHON: "C:\\Python{{ env[:3].replace('.', '') }}-x64\\python.exe" - WINDOWS_SDK_VERSION: "v7.{{ '1' if env[0] == '3' else '0' }}" - PYTHON_HOME: "C:\\Python{{ env[:3].replace('.', '') }}-x64" - PYTHON_VERSION: "{{ env[:3] }}" - PYTHON_ARCH: "64" + PYTHON_HOME: C:\Python27 + PYTHON_VERSION: '2.7' + PYTHON_ARCH: '32' + +{% for env, config in tox_environments|dictsort %}{% if config.python in ('python2.7', 'python3.3', 'python3.4', 'python3.5') %} + - TOXENV: '{{ env }}{% if config.cover %},codecov{% endif %}' + TOXPYTHON: C:\{{ config.python.replace('.', '').capitalize() }}\python.exe + PYTHON_HOME: C:\{{ config.python.replace('.', '').capitalize() }} + PYTHON_VERSION: '{{ config.python[-3:] }}' + PYTHON_ARCH: '32' + + - TOXENV: '{{ env }}{% if config.cover %},codecov{% endif %}' + TOXPYTHON: C:\{{ config.python.replace('.', '').capitalize() }}-x64\python.exe + {%- if config.python != 'python3.5' %} + + WINDOWS_SDK_VERSION: v7.{{ '1' if config.python[-3] == '3' else '0' }} + {%- endif %} + + PYTHON_HOME: C:\{{ config.python.replace('.', '').capitalize() }}-x64 + PYTHON_VERSION: '{{ config.python[-3:] }}' + PYTHON_ARCH: '64' + {% endif %}{% endfor %} init: - - "ECHO %TOXENV%" - - ps: "ls C:\\Python*" + - ps: echo $env:TOXENV + - ps: ls C:\Python* install: - - "powershell ci\\appveyor-bootstrap.ps1" + - python -u ci\appveyor-bootstrap.py + - '%PYTHON_HOME%\Scripts\virtualenv --version' + - '%PYTHON_HOME%\Scripts\easy_install --version' + - '%PYTHON_HOME%\Scripts\pip --version' + - '%PYTHON_HOME%\Scripts\tox --version' test_script: - - "%PYTHON_HOME%\\Scripts\\tox --version" - - "%PYTHON_HOME%\\Scripts\\virtualenv --version" - - "%PYTHON_HOME%\\Scripts\\pip --version" - - "%WITH_COMPILER% %PYTHON_HOME%\\Scripts\\tox" -after_test: - - "IF \"%TOXENV:~-8,8%\" == \"-nocover\" %WITH_COMPILER% %TOXPYTHON% setup.py bdist_wheel" + - '%WITH_COMPILER% %PYTHON_HOME%\Scripts\tox' + +on_failure: + - ps: dir "env:" + - ps: get-content .tox\*\log\* artifacts: - path: dist\* +### To enable remote debugging uncomment this (also, see: http://www.appveyor.com/docs/how-to/rdp-to-build-worker): +# on_finish: +# - ps: $blockRdp = $true; iex ((new-object net.webclient).DownloadString('https://raw.githubusercontent.com/appveyor/ci/master/scripts/enable-rdp.ps1')) diff --git a/ci/templates/tox.ini b/ci/templates/tox.ini index 0c65548..a6130d7 100644 --- a/ci/templates/tox.ini +++ b/ci/templates/tox.ini @@ -9,6 +9,9 @@ envlist = docs [testenv] +basepython = + {docs,spell}: python2.7 + {clean,check,report,extension-coveralls,coveralls,codecov}: python3.5 setenv = PYTHONPATH={toxinidir}/tests PYTHONUNBUFFERED=yes @@ -16,12 +19,7 @@ passenv = * deps = pytest - pytest-capturelog - process-tests - nose - mock - tornado - profilestats + pytest-travis-fold commands = {posargs:py.test -vv --ignore=src} @@ -31,7 +29,7 @@ setenv = commands = sphinx-build -b spelling docs dist/docs skip_install = true -usedevelop = true +usedevelop = false deps = -r{toxinidir}/docs/requirements.txt sphinxcontrib-spelling @@ -56,19 +54,20 @@ passenv = * [testenv:check] -basepython = python3.4 deps = docutils check-manifest flake8 - readme + readme-renderer pygments + isort skip_install = true usedevelop = false commands = python setup.py check --strict --metadata --restructuredtext check-manifest {toxinidir} - flake8 src tests + flake8 src tests setup.py + isort --verbose --check-only --diff --recursive src tests setup.py [testenv:coveralls] deps = @@ -93,13 +92,13 @@ commands = [testenv:report] -basepython = python3.4 deps = coverage skip_install = true usedevelop = false commands = coverage combine coverage report + coverage html [testenv:clean] commands = coverage erase diff --git a/docs/conf.py b/docs/conf.py index b048be2..87b5930 100644 --- a/docs/conf.py +++ b/docs/conf.py @@ -3,12 +3,13 @@ import os -import fields + extensions = [ 'sphinx.ext.autodoc', 'sphinx.ext.autosummary', 'sphinx.ext.coverage', 'sphinx.ext.doctest', + 'sphinx.ext.extlinks', 'sphinx.ext.ifconfig', 'sphinx.ext.napoleon', 'sphinx.ext.todo', @@ -22,10 +23,17 @@ source_suffix = '.rst' master_doc = 'index' project = 'Aspectlib' -year = '2014-2015' +year = '2014-2016' author = 'Ionel Cristian Mărieș' copyright = '{0}, {1}'.format(year, author) version = release = '1.3.3' + +pygments_style = 'trac' +templates_path = ['.'] +extlinks = { + 'issue': ('https://github.com/ionelmc/python-aspectlib/issues/%s', '#'), + 'pr': ('https://github.com/ionelmc/python-aspectlib/pull/%s', 'PR #'), +} import sphinx_py3doc_enhanced_theme html_theme = "sphinx_py3doc_enhanced_theme" html_theme_path = [sphinx_py3doc_enhanced_theme.get_html_theme_path()] @@ -33,8 +41,6 @@ 'githuburl': 'https://github.com/ionelmc/python-aspectlib/' } -pygments_style = 'trac' -templates_path = ['.'] html_use_smartypants = True html_last_updated_fmt = '%b %d, %Y' html_split_index = True @@ -42,3 +48,7 @@ '**': ['searchbox.html', 'globaltoc.html', 'sourcelink.html'], } html_short_title = '%s-%s' % (project, version) + +napoleon_use_ivar = True +napoleon_use_rtype = False +napoleon_use_param = False diff --git a/setup.cfg b/setup.cfg index 18218ad..09af5e3 100644 --- a/setup.cfg +++ b/setup.cfg @@ -1,9 +1,6 @@ [bdist_wheel] universal = 1 -[aliases] -release = register clean --all sdist bdist_wheel - [flake8] max-line-length = 140 exclude = tests/*,*/migrations/*,*/south_migrations/* @@ -38,6 +35,8 @@ line_length=120 known_first_party=aspectlib default_section=THIRDPARTY forced_separate=test_aspectlib +not_skip = __init__.py +skip = migrations, south_migrations [matrix] # This is the configuration for the `./bootstrap.py` script. diff --git a/setup.py b/setup.py index d2fdae1..5a9ec84 100644 --- a/setup.py +++ b/setup.py @@ -1,14 +1,14 @@ #!/usr/bin/env python # -*- encoding: utf-8 -*- -from __future__ import absolute_import, print_function +from __future__ import absolute_import +from __future__ import print_function + import io -import os import re from glob import glob from os.path import basename from os.path import dirname from os.path import join -from os.path import relpath from os.path import splitext from setuptools import find_packages @@ -25,10 +25,11 @@ def read(*names, **kwargs): name='aspectlib', version='1.3.3', license='BSD', - description="aspectlib is an aspect-oriented programming, monkey-patch and decorators library. It is useful when " - "changing behavior in existing code is desired. It includes tools for debugging and testing: simple " - "mock/record and a complete capture/replay framework.", - long_description='%s\n%s' % (read('README.rst'), re.sub(':[a-z]+:`~?(.*?)`', r'``\1``', read('CHANGELOG.rst'))), + description='``aspectlib`` is an aspect-oriented programming, monkey-patch and decorators library. It is useful when changing', + long_description='%s\n%s' % ( + re.compile('^.. start-badges.*^.. end-badges', re.M | re.S).sub('', read('README.rst')), + re.sub(':[a-z]+:`~?(.*?)`', r'``\1``', read('CHANGELOG.rst')) + ), author='Ionel Cristian Mărieș', author_email='contact@ionelmc.ro', url='https://github.com/ionelmc/python-aspectlib', diff --git a/tox.ini b/tox.ini index 7328b29..04999e8 100644 --- a/tox.ini +++ b/tox.ini @@ -26,6 +26,9 @@ envlist = docs [testenv] +basepython = + {docs,spell}: python2.7 + {clean,check,report,extension-coveralls,coveralls,codecov}: python3.5 setenv = PYTHONPATH={toxinidir}/tests PYTHONUNBUFFERED=yes @@ -33,12 +36,7 @@ passenv = * deps = pytest - pytest-capturelog - process-tests - nose - mock - tornado - profilestats + pytest-travis-fold commands = {posargs:py.test -vv --ignore=src} @@ -48,7 +46,7 @@ setenv = commands = sphinx-build -b spelling docs dist/docs skip_install = true -usedevelop = true +usedevelop = false deps = -r{toxinidir}/docs/requirements.txt sphinxcontrib-spelling @@ -73,19 +71,20 @@ passenv = * [testenv:check] -basepython = python3.4 deps = docutils check-manifest flake8 - readme + readme-renderer pygments + isort skip_install = true usedevelop = false commands = python setup.py check --strict --metadata --restructuredtext check-manifest {toxinidir} - flake8 src tests + flake8 src tests setup.py + isort --verbose --check-only --diff --recursive src tests setup.py [testenv:coveralls] deps = @@ -110,13 +109,13 @@ commands = [testenv:report] -basepython = python3.4 deps = coverage skip_install = true usedevelop = false commands = coverage combine coverage report + coverage html [testenv:clean] commands = coverage erase From ac2fb06ea060ddc1f136662ffc3dc50e31a08434 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ionel=20Cristian=20M=C4=83rie=C8=99?= Date: Sat, 9 Apr 2016 18:44:09 +0300 Subject: [PATCH 133/179] Fix clearing. --- src/aspectlib/__init__.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/aspectlib/__init__.py b/src/aspectlib/__init__.py index ae94641..297bd7e 100644 --- a/src/aspectlib/__init__.py +++ b/src/aspectlib/__init__.py @@ -307,7 +307,7 @@ def __enter__(self): def __exit__(self, *_): for rollback in self._rollbacks: rollback() - self._rollbacks.clear() + del self._rollbacks[:] rollback = __call__ = __exit__ From f6254f80495ced660b86a6f998caf1d0717646af Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ionel=20Cristian=20M=C4=83rie=C8=99?= Date: Sat, 9 Apr 2016 18:54:43 +0300 Subject: [PATCH 134/179] Fix missing deps. --- ci/templates/tox.ini | 6 ++++++ tox.ini | 6 ++++++ 2 files changed, 12 insertions(+) diff --git a/ci/templates/tox.ini b/ci/templates/tox.ini index a6130d7..ab65608 100644 --- a/ci/templates/tox.ini +++ b/ci/templates/tox.ini @@ -20,6 +20,12 @@ passenv = deps = pytest pytest-travis-fold + pytest-catchlog + process-tests + nose + mock + tornado + profilestats commands = {posargs:py.test -vv --ignore=src} diff --git a/tox.ini b/tox.ini index 04999e8..594d288 100644 --- a/tox.ini +++ b/tox.ini @@ -37,6 +37,12 @@ passenv = deps = pytest pytest-travis-fold + pytest-catchlog + process-tests + nose + mock + tornado + profilestats commands = {posargs:py.test -vv --ignore=src} From e8dc26889c1a12bfbddc0e170adec4202b1272f3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ionel=20Cristian=20M=C4=83rie=C8=99?= Date: Sat, 9 Apr 2016 18:54:50 +0300 Subject: [PATCH 135/179] Sort imports. --- src/aspectlib/__init__.py | 17 +++++++++-------- src/aspectlib/contrib.py | 2 +- src/aspectlib/py3support.py | 2 +- src/aspectlib/test.py | 2 +- src/aspectlib/utils.py | 5 ++--- tests/test_aspectlib_debug.py | 9 +++++---- tests/test_aspectlib_test.py | 8 ++++---- tests/test_integrations_py2.py | 9 +++++---- tests/test_integrations_py3.py | 2 +- 9 files changed, 29 insertions(+), 27 deletions(-) diff --git a/src/aspectlib/__init__.py b/src/aspectlib/__init__.py index 297bd7e..1db5c19 100644 --- a/src/aspectlib/__init__.py +++ b/src/aspectlib/__init__.py @@ -4,6 +4,7 @@ import sys import warnings from collections import deque +from functools import partial from inspect import isclass from inspect import isfunction from inspect import isgenerator @@ -12,21 +13,21 @@ from inspect import ismethoddescriptor from inspect import ismodule from inspect import isroutine -try: - from types import InstanceType -except ImportError: - InstanceType = None -from functools import partial from logging import getLogger +from .utils import PY2 +from .utils import PY3 +from .utils import Sentinel from .utils import basestring from .utils import force_bind from .utils import logf from .utils import make_method_matcher from .utils import mimic -from .utils import PY2 -from .utils import PY3 -from .utils import Sentinel + +try: + from types import InstanceType +except ImportError: + InstanceType = None try: import __builtin__ diff --git a/src/aspectlib/contrib.py b/src/aspectlib/contrib.py index 2190550..b4edb5b 100644 --- a/src/aspectlib/contrib.py +++ b/src/aspectlib/contrib.py @@ -1,5 +1,5 @@ -from logging import getLogger import time +from logging import getLogger from aspectlib import Aspect diff --git a/src/aspectlib/py3support.py b/src/aspectlib/py3support.py index 126abf3..db9f907 100644 --- a/src/aspectlib/py3support.py +++ b/src/aspectlib/py3support.py @@ -6,10 +6,10 @@ from logging import getLogger from aspectlib import ExpectedGenerator -from aspectlib import mimic from aspectlib import Proceed from aspectlib import Return from aspectlib import UnacceptableAdvice +from aspectlib import mimic from aspectlib.utils import logf logger = getLogger(__name__) diff --git a/src/aspectlib/test.py b/src/aspectlib/test.py index e689efc..3fc72c8 100644 --- a/src/aspectlib/test.py +++ b/src/aspectlib/test.py @@ -13,12 +13,12 @@ from aspectlib import mimic from aspectlib import weave +from .utils import Sentinel from .utils import camelcase_to_underscores from .utils import container from .utils import logf from .utils import qualname from .utils import repr_ex -from .utils import Sentinel try: from logging import _levelNames as nameToLevel diff --git a/src/aspectlib/utils.py b/src/aspectlib/utils.py index 3d2a389..ac93440 100644 --- a/src/aspectlib/utils.py +++ b/src/aspectlib/utils.py @@ -1,15 +1,14 @@ from __future__ import print_function -import platform import logging -import re import os +import platform +import re import sys from collections import deque from functools import wraps from inspect import isclass - RegexType = type(re.compile("")) PY3 = sys.version_info[0] == 3 diff --git a/tests/test_aspectlib_debug.py b/tests/test_aspectlib_debug.py index 1114c16..631e6bb 100644 --- a/tests/test_aspectlib_debug.py +++ b/tests/test_aspectlib_debug.py @@ -4,16 +4,17 @@ import re import sys import weakref -try: - from StringIO import StringIO -except ImportError: - from io import StringIO import pytest import aspectlib import aspectlib.debug +try: + from StringIO import StringIO +except ImportError: + from io import StringIO + LOG_TEST_SIMPLE = r'''^some_meth\(1, 2, 3, a=4\) +<<< .*tests/test_aspectlib_debug.py:\d+:test_simple.* some_meth => \.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\. !"#\$%&\'\(\)\*\+,-\./0123456789:;<=>\?@ABCDEFGHIJKLMNOPQRSTUVWXYZ\[\\\]\^_`abcdefghijklmnopqrstuvwxyz\{\|\}~\.+ diff --git a/tests/test_aspectlib_test.py b/tests/test_aspectlib_test.py index f3891a4..95c893a 100644 --- a/tests/test_aspectlib_test.py +++ b/tests/test_aspectlib_test.py @@ -1,20 +1,20 @@ from __future__ import print_function from pytest import raises +from test_pkg1.test_pkg2 import test_mod from aspectlib import PY2 +from aspectlib.test import OrderedDict +from aspectlib.test import Story +from aspectlib.test import StoryResultWrapper from aspectlib.test import _Binds from aspectlib.test import _format_calls from aspectlib.test import _Raises from aspectlib.test import _Returns from aspectlib.test import mock -from aspectlib.test import OrderedDict from aspectlib.test import record -from aspectlib.test import Story -from aspectlib.test import StoryResultWrapper from aspectlib.utils import PY26 from aspectlib.utils import repr_ex -from test_pkg1.test_pkg2 import test_mod format_calls = lambda calls: ''.join(_format_calls(calls)) diff --git a/tests/test_integrations_py2.py b/tests/test_integrations_py2.py index 1cc5fb2..ceeda45 100644 --- a/tests/test_integrations_py2.py +++ b/tests/test_integrations_py2.py @@ -3,19 +3,20 @@ except ImportError: from io import StringIO from datetime import timedelta + import pytest -import sys import trollius as asyncio from tornado import gen from tornado import ioloop + +from aspectlib import debug +from aspectlib.test import Story + try: import MySQLdb except ImportError: MySQLdb = None -from aspectlib import debug, weave, ALL_METHODS -from aspectlib.test import Story - def test_decorate_asyncio_coroutine(): buf = StringIO() diff --git a/tests/test_integrations_py3.py b/tests/test_integrations_py3.py index 9e54fe9..898c0ed 100644 --- a/tests/test_integrations_py3.py +++ b/tests/test_integrations_py3.py @@ -2,9 +2,9 @@ from StringIO import StringIO except ImportError: from io import StringIO +import asyncio from datetime import timedelta -import asyncio from tornado import gen from tornado import ioloop From eee8b564f6aa6a540e0ff3763331836a7811ac54 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ionel=20Cristian=20M=C4=83rie=C8=99?= Date: Sat, 9 Apr 2016 23:51:01 +0300 Subject: [PATCH 136/179] Don't split index anymore. --- docs/conf.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/conf.py b/docs/conf.py index 87b5930..4061549 100644 --- a/docs/conf.py +++ b/docs/conf.py @@ -43,7 +43,7 @@ html_use_smartypants = True html_last_updated_fmt = '%b %d, %Y' -html_split_index = True +html_split_index = False html_sidebars = { '**': ['searchbox.html', 'globaltoc.html', 'sourcelink.html'], } From e2f7664c3a2b5216bd6bf261491f5457c411bd3f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ionel=20Cristian=20M=C4=83rie=C8=99?= Date: Sun, 10 Apr 2016 02:52:24 +0300 Subject: [PATCH 137/179] =?UTF-8?q?Bump=20version:=201.3.3=20=E2=86=92=201?= =?UTF-8?q?.4.0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .bumpversion.cfg | 2 +- docs/conf.py | 2 +- setup.py | 2 +- src/aspectlib/__init__.py | 2 +- 4 files changed, 4 insertions(+), 4 deletions(-) diff --git a/.bumpversion.cfg b/.bumpversion.cfg index 694d024..09423e6 100644 --- a/.bumpversion.cfg +++ b/.bumpversion.cfg @@ -1,5 +1,5 @@ [bumpversion] -current_version = 1.3.3 +current_version = 1.4.0 commit = True tag = True diff --git a/docs/conf.py b/docs/conf.py index 4061549..040da17 100644 --- a/docs/conf.py +++ b/docs/conf.py @@ -26,7 +26,7 @@ year = '2014-2016' author = 'Ionel Cristian Mărieș' copyright = '{0}, {1}'.format(year, author) -version = release = '1.3.3' +version = release = '1.4.0' pygments_style = 'trac' templates_path = ['.'] diff --git a/setup.py b/setup.py index 5a9ec84..3b26d9c 100644 --- a/setup.py +++ b/setup.py @@ -23,7 +23,7 @@ def read(*names, **kwargs): setup( name='aspectlib', - version='1.3.3', + version='1.4.0', license='BSD', description='``aspectlib`` is an aspect-oriented programming, monkey-patch and decorators library. It is useful when changing', long_description='%s\n%s' % ( diff --git a/src/aspectlib/__init__.py b/src/aspectlib/__init__.py index 1db5c19..0af18bd 100644 --- a/src/aspectlib/__init__.py +++ b/src/aspectlib/__init__.py @@ -41,7 +41,7 @@ __all__ = 'weave', 'Aspect', 'Proceed', 'Return', 'ALL_METHODS', 'NORMAL_METHODS', 'ABSOLUTELY_ALL_METHODS' -__version__ = '1.3.3' +__version__ = '1.4.0' logger = getLogger(__name__) logdebug = logf(logger.debug) From 6a3298ec92fbc6ecca77a269584e822705a892bf Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ionel=20Cristian=20M=C4=83rie=C8=99?= Date: Fri, 6 May 2016 20:34:20 +0300 Subject: [PATCH 138/179] Fix importing of modules with dots in certain scenarios. Ref #6. --- src/aspectlib/__init__.py | 19 ++++++++++++------- tests/test_aspectlib.py | 8 ++++++++ 2 files changed, 20 insertions(+), 7 deletions(-) diff --git a/src/aspectlib/__init__.py b/src/aspectlib/__init__.py index 0af18bd..4fb3f6f 100644 --- a/src/aspectlib/__init__.py +++ b/src/aspectlib/__init__.py @@ -417,14 +417,12 @@ def weave(target, aspects, **options): _check_name(part) if len(parts) == 1: - __import__(part) - return weave_module(sys.modules[part], aspects, **options) + return weave_module(_import_module(part), aspects, **options) for pos in reversed(range(1, len(parts))): owner, name = '.'.join(parts[:pos]), '.'.join(parts[pos:]) try: - __import__(owner) - owner = sys.modules[owner] + owner = _import_module(owner) except ImportError: continue else: @@ -472,10 +470,12 @@ def weave(target, aspects, **options): elif PY3 and isfunction(target): if bag.has(target): return Nothing - owner = __import__(target.__module__) + owner = _import_module(target.__module__) path = deque(target.__qualname__.split('.')[:-1]) + print(owner, target.__module__) while path: owner = getattr(owner, path.popleft()) + print(owner, ) name = target.__name__ logdebug("@ patching %r (%s) as a property.", target, name) func = owner.__dict__[name] @@ -483,7 +483,7 @@ def weave(target, aspects, **options): elif PY2 and isfunction(target): if bag.has(target): return Nothing - return patch_module_function(__import__(target.__module__), target, aspects, **options) + return patch_module_function(_import_module(target.__module__), target, aspects, **options) elif PY2 and ismethod(target): if target.im_self: if bag.has(target): @@ -633,7 +633,7 @@ def __init__(self, *args, **kwargs): name = name or klass.__name__ SubClass = type(name, (klass, Fabric), wrappers) SubClass.__module__ = klass.__module__ - module = owner or __import__(klass.__module__) + module = owner or _import_module(klass.__module__) entanglement.merge(patch_module(module, name, SubClass, original=klass, aliases=aliases)) else: original = {} @@ -679,6 +679,11 @@ def _find_super_classes(klass): yield k +def _import_module(module): + __import__(module) + return sys.modules[module] + + def patch_module(module, name, replacement, original=UNSPECIFIED, aliases=True, location=None, **_bogus_options): """ Low-level attribute patcher. diff --git a/tests/test_aspectlib.py b/tests/test_aspectlib.py index d55c9c2..fa5c802 100644 --- a/tests/test_aspectlib.py +++ b/tests/test_aspectlib.py @@ -1687,3 +1687,11 @@ def foo(self, arg): assert calls == [(f, 1)] assert intercepted == [(f, 1)] + + +def test_weave_debug_log(): + from aspectlib.contrib import retry + from aspectlib.debug import log + + with aspectlib.weave(log, retry): + pass From f9fd57bf887b0dcdcd43d4bc9a8ce28c29321650 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ionel=20Cristian=20M=C4=83rie=C8=99?= Date: Fri, 6 May 2016 21:30:13 +0300 Subject: [PATCH 139/179] Update changelog.t --- CHANGELOG.rst | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/CHANGELOG.rst b/CHANGELOG.rst index 5f83012..3477a95 100644 --- a/CHANGELOG.rst +++ b/CHANGELOG.rst @@ -1,6 +1,11 @@ Changelog ========= +1.4.1 (2016-05-06) +------------------ + +* Fixed weaving of objects that don't live on root-level modules. + 1.4.0 (2016-04-09) ------------------ From e50e1f6aeb80ea3cfc7ba934cc1ff38d871faf4a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ionel=20Cristian=20M=C4=83rie=C8=99?= Date: Fri, 6 May 2016 21:45:31 +0300 Subject: [PATCH 140/179] =?UTF-8?q?Bump=20version:=201.4.0=20=E2=86=92=201?= =?UTF-8?q?.4.1?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .bumpversion.cfg | 2 +- docs/conf.py | 2 +- setup.py | 2 +- src/aspectlib/__init__.py | 2 +- 4 files changed, 4 insertions(+), 4 deletions(-) diff --git a/.bumpversion.cfg b/.bumpversion.cfg index 09423e6..302f321 100644 --- a/.bumpversion.cfg +++ b/.bumpversion.cfg @@ -1,5 +1,5 @@ [bumpversion] -current_version = 1.4.0 +current_version = 1.4.1 commit = True tag = True diff --git a/docs/conf.py b/docs/conf.py index 040da17..e0f9921 100644 --- a/docs/conf.py +++ b/docs/conf.py @@ -26,7 +26,7 @@ year = '2014-2016' author = 'Ionel Cristian Mărieș' copyright = '{0}, {1}'.format(year, author) -version = release = '1.4.0' +version = release = '1.4.1' pygments_style = 'trac' templates_path = ['.'] diff --git a/setup.py b/setup.py index 3b26d9c..a0ddde7 100644 --- a/setup.py +++ b/setup.py @@ -23,7 +23,7 @@ def read(*names, **kwargs): setup( name='aspectlib', - version='1.4.0', + version='1.4.1', license='BSD', description='``aspectlib`` is an aspect-oriented programming, monkey-patch and decorators library. It is useful when changing', long_description='%s\n%s' % ( diff --git a/src/aspectlib/__init__.py b/src/aspectlib/__init__.py index 4fb3f6f..2708940 100644 --- a/src/aspectlib/__init__.py +++ b/src/aspectlib/__init__.py @@ -41,7 +41,7 @@ __all__ = 'weave', 'Aspect', 'Proceed', 'Return', 'ALL_METHODS', 'NORMAL_METHODS', 'ABSOLUTELY_ALL_METHODS' -__version__ = '1.4.0' +__version__ = '1.4.1' logger = getLogger(__name__) logdebug = logf(logger.debug) From 8cec64ae517b5a74548c9b0a7fd71d908a2c03cd Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ionel=20Cristian=20M=C4=83rie=C8=99?= Date: Tue, 10 May 2016 16:27:31 +0300 Subject: [PATCH 141/179] Oooops. --- CHANGELOG.rst | 5 +++++ src/aspectlib/__init__.py | 2 -- 2 files changed, 5 insertions(+), 2 deletions(-) diff --git a/CHANGELOG.rst b/CHANGELOG.rst index 3477a95..4bff9bc 100644 --- a/CHANGELOG.rst +++ b/CHANGELOG.rst @@ -1,6 +1,11 @@ Changelog ========= +1.4.2 (2016-05-10) +------------------ + +* Fix forgotten debug prints. + 1.4.1 (2016-05-06) ------------------ diff --git a/src/aspectlib/__init__.py b/src/aspectlib/__init__.py index 2708940..7bc0a30 100644 --- a/src/aspectlib/__init__.py +++ b/src/aspectlib/__init__.py @@ -472,10 +472,8 @@ def weave(target, aspects, **options): return Nothing owner = _import_module(target.__module__) path = deque(target.__qualname__.split('.')[:-1]) - print(owner, target.__module__) while path: owner = getattr(owner, path.popleft()) - print(owner, ) name = target.__name__ logdebug("@ patching %r (%s) as a property.", target, name) func = owner.__dict__[name] From 0ca9775c610d29a9105f2c4d04b18c7970b6da80 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ionel=20Cristian=20M=C4=83rie=C8=99?= Date: Tue, 10 May 2016 16:28:00 +0300 Subject: [PATCH 142/179] =?UTF-8?q?Bump=20version:=201.4.1=20=E2=86=92=201?= =?UTF-8?q?.4.2?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .bumpversion.cfg | 2 +- docs/conf.py | 2 +- setup.py | 2 +- src/aspectlib/__init__.py | 2 +- 4 files changed, 4 insertions(+), 4 deletions(-) diff --git a/.bumpversion.cfg b/.bumpversion.cfg index 302f321..68c0247 100644 --- a/.bumpversion.cfg +++ b/.bumpversion.cfg @@ -1,5 +1,5 @@ [bumpversion] -current_version = 1.4.1 +current_version = 1.4.2 commit = True tag = True diff --git a/docs/conf.py b/docs/conf.py index e0f9921..8770587 100644 --- a/docs/conf.py +++ b/docs/conf.py @@ -26,7 +26,7 @@ year = '2014-2016' author = 'Ionel Cristian Mărieș' copyright = '{0}, {1}'.format(year, author) -version = release = '1.4.1' +version = release = '1.4.2' pygments_style = 'trac' templates_path = ['.'] diff --git a/setup.py b/setup.py index a0ddde7..d48497b 100644 --- a/setup.py +++ b/setup.py @@ -23,7 +23,7 @@ def read(*names, **kwargs): setup( name='aspectlib', - version='1.4.1', + version='1.4.2', license='BSD', description='``aspectlib`` is an aspect-oriented programming, monkey-patch and decorators library. It is useful when changing', long_description='%s\n%s' % ( diff --git a/src/aspectlib/__init__.py b/src/aspectlib/__init__.py index 7bc0a30..33102cd 100644 --- a/src/aspectlib/__init__.py +++ b/src/aspectlib/__init__.py @@ -41,7 +41,7 @@ __all__ = 'weave', 'Aspect', 'Proceed', 'Return', 'ALL_METHODS', 'NORMAL_METHODS', 'ABSOLUTELY_ALL_METHODS' -__version__ = '1.4.1' +__version__ = '1.4.2' logger = getLogger(__name__) logdebug = logf(logger.debug) From a7cac4933b607420fdd55e6e165302c98dd7f22a Mon Sep 17 00:00:00 2001 From: German Robayo Date: Sat, 17 Nov 2018 15:32:24 -0400 Subject: [PATCH 143/179] fix introduction example of aspectlib.Aspect --- docs/introduction.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/introduction.rst b/docs/introduction.rst index fdaa992..b650280 100644 --- a/docs/introduction.rst +++ b/docs/introduction.rst @@ -19,7 +19,7 @@ Example: .. code-block:: python @aspectlib.Aspect - def strip_return_value(): + def strip_return_value(*args, **kwargs): result = yield aspectlib.Proceed yield aspectlib.Return(result.strip()) From 7576ced95be84815e8fc4fff2c24be6317b5fb9c Mon Sep 17 00:00:00 2001 From: Jonas Maurus Date: Sat, 15 Jun 2019 22:50:10 +0200 Subject: [PATCH 144/179] Only evaluate attributes after we know they match the method selector. Otherwise properties on objects might trigger even though they're not matched by an aspect. It's unavoidable for those attributes that do match the method selector, but we don't have to read unrelated attributes. --- src/aspectlib/__init__.py | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/src/aspectlib/__init__.py b/src/aspectlib/__init__.py index 33102cd..5ea9f0b 100644 --- a/src/aspectlib/__init__.py +++ b/src/aspectlib/__init__.py @@ -542,8 +542,8 @@ def fixup(func): fixed_aspect = aspect + [fixup] if isinstance(aspect, (list, tuple)) else [aspect, fixup] for attr in dir(instance): - func = getattr(instance, attr) if method_matches(attr): + func = getattr(instance, attr) if ismethod(func): if hasattr(func, '__func__'): realfunc = func.__func__ @@ -572,8 +572,8 @@ def weave_module(module, aspect, methods=NORMAL_METHODS, lazy=False, bag=BrokenB module, aspect, methods, lazy, options) for attr in dir(module): - func = getattr(module, attr) if method_matches(attr): + func = getattr(module, attr) if isroutine(func): entanglement.merge(patch_module_function(module, func, aspect, force_name=attr, **options)) elif isclass(func): @@ -615,9 +615,10 @@ def weave_class(klass, aspect, methods=NORMAL_METHODS, subclasses=True, lazy=Fal def __init__(self, *args, **kwargs): super(SubClass, self).__init__(*args, **kwargs) for attr in dir(self): - func = getattr(self, attr, None) - if method_matches(attr) and attr not in wrappers and isroutine(func): - setattr(self, attr, _checked_apply(aspect, force_bind(func)).__get__(self, SubClass)) + if method_matches(attr) and attr not in wrappers: + func = getattr(self, attr, None) + if isroutine(func): + setattr(self, attr, _checked_apply(aspect, force_bind(func)).__get__(self, SubClass)) wrappers = { '__init__': _checked_apply(aspect, __init__) if method_matches('__init__') else __init__ From 66efa2ee31ffc6de34eeff9cfec1676acd506894 Mon Sep 17 00:00:00 2001 From: Jonas Maurus Date: Fri, 19 Jul 2019 14:57:25 +0200 Subject: [PATCH 145/179] update authors and changelog information --- AUTHORS.rst | 1 + CHANGELOG.rst | 5 +++++ 2 files changed, 6 insertions(+) diff --git a/AUTHORS.rst b/AUTHORS.rst index d01607a..24b749f 100644 --- a/AUTHORS.rst +++ b/AUTHORS.rst @@ -3,3 +3,4 @@ Authors ======= * Ionel Cristian Mărieș - https://blog.ionelmc.ro +* Jonas Maurus - https://github.com/jdelic/ diff --git a/CHANGELOG.rst b/CHANGELOG.rst index 4bff9bc..0d8eb69 100644 --- a/CHANGELOG.rst +++ b/CHANGELOG.rst @@ -1,6 +1,11 @@ Changelog ========= +unreleased (2019-07-19) +----------------------- + +* During weaving, stop reading attributes don't match the method selector. + 1.4.2 (2016-05-10) ------------------ From a371584e36811f384f780b9fd5e43f6e50f1d731 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ionel=20Cristian=20M=C4=83rie=C8=99?= Date: Tue, 31 Mar 2020 15:47:23 +0300 Subject: [PATCH 146/179] Update project skel. --- .appveyor.yml | 254 +++++++++++++++++++++++++++++++ .bumpversion.cfg | 11 +- .cookiecutterrc | 87 +++++++---- .coveragerc | 10 +- .editorconfig | 5 +- .gitignore | 8 + .pre-commit-config.yaml | 19 +++ .readthedocs.yml | 10 ++ .travis.yml | 133 ++++++++++++---- CONTRIBUTING.rst | 6 +- LICENSE | 4 +- MANIFEST.in | 8 +- README.rst | 48 ++---- appveyor.yml | 238 ----------------------------- ci/appveyor-bootstrap.py | 113 -------------- ci/appveyor-with-compiler.cmd | 39 +---- ci/bootstrap.py | 68 ++++++--- ci/requirements.txt | 4 + ci/templates/.appveyor.yml | 52 +++++++ ci/templates/.travis.yml | 40 +++-- ci/templates/appveyor.yml | 53 ------- ci/templates/tox.ini | 79 ++++------ conftest.py | 2 + docs/conf.py | 11 +- setup.cfg | 55 +++---- setup.py | 24 ++- src/aspectlib/__init__.py | 4 +- src/aspectlib/contrib.py | 6 + src/aspectlib/debug.py | 1 + src/aspectlib/pytestsupport.py | 1 + src/aspectlib/test.py | 3 +- src/aspectlib/utils.py | 4 +- tox.ini | 268 ++++++++++++++++++--------------- 33 files changed, 865 insertions(+), 803 deletions(-) create mode 100644 .appveyor.yml create mode 100644 .pre-commit-config.yaml create mode 100644 .readthedocs.yml delete mode 100644 appveyor.yml delete mode 100644 ci/appveyor-bootstrap.py create mode 100644 ci/requirements.txt create mode 100644 ci/templates/.appveyor.yml delete mode 100644 ci/templates/appveyor.yml mode change 100644 => 100755 setup.py diff --git a/.appveyor.yml b/.appveyor.yml new file mode 100644 index 0000000..970b5a3 --- /dev/null +++ b/.appveyor.yml @@ -0,0 +1,254 @@ +version: '{branch}-{build}' +build: off +environment: + global: + COVERALLS_EXTRAS: '-v' + COVERALLS_REPO_TOKEN: mHSWktPkrw65WSlziG8NnK9m1xj2k1kOQ + matrix: + - TOXENV: check + TOXPYTHON: C:\Python36\python.exe + PYTHON_HOME: C:\Python36 + PYTHON_VERSION: '3.6' + PYTHON_ARCH: '32' + - TOXENV: py27-cover,codecov,coveralls + TOXPYTHON: C:\Python27\python.exe + PYTHON_HOME: C:\Python27 + PYTHON_VERSION: '2.7' + PYTHON_ARCH: '32' + - TOXENV: py27-cover,codecov,coveralls + TOXPYTHON: C:\Python27-x64\python.exe + PYTHON_HOME: C:\Python27-x64 + PYTHON_VERSION: '2.7' + PYTHON_ARCH: '64' + WINDOWS_SDK_VERSION: v7.0 + - TOXENV: py27-cover-debug,codecov,coveralls + TOXPYTHON: C:\Python27\python.exe + PYTHON_HOME: C:\Python27 + PYTHON_VERSION: '2.7' + PYTHON_ARCH: '32' + - TOXENV: py27-cover-debug,codecov,coveralls + TOXPYTHON: C:\Python27-x64\python.exe + PYTHON_HOME: C:\Python27-x64 + PYTHON_VERSION: '2.7' + PYTHON_ARCH: '64' + WINDOWS_SDK_VERSION: v7.0 + - TOXENV: py27-nocov,codecov,coveralls + TOXPYTHON: C:\Python27\python.exe + PYTHON_HOME: C:\Python27 + PYTHON_VERSION: '2.7' + PYTHON_ARCH: '32' + WHEEL_PATH: .tox/dist + - TOXENV: py27-nocov,codecov,coveralls + TOXPYTHON: C:\Python27-x64\python.exe + PYTHON_HOME: C:\Python27-x64 + PYTHON_VERSION: '2.7' + PYTHON_ARCH: '64' + WHEEL_PATH: .tox/dist + WINDOWS_SDK_VERSION: v7.0 + - TOXENV: py27-nocov-debug,codecov,coveralls + TOXPYTHON: C:\Python27\python.exe + PYTHON_HOME: C:\Python27 + PYTHON_VERSION: '2.7' + PYTHON_ARCH: '32' + WHEEL_PATH: .tox/dist + - TOXENV: py27-nocov-debug,codecov,coveralls + TOXPYTHON: C:\Python27-x64\python.exe + PYTHON_HOME: C:\Python27-x64 + PYTHON_VERSION: '2.7' + PYTHON_ARCH: '64' + WHEEL_PATH: .tox/dist + WINDOWS_SDK_VERSION: v7.0 + - TOXENV: py35-cover,codecov,coveralls + TOXPYTHON: C:\Python35\python.exe + PYTHON_HOME: C:\Python35 + PYTHON_VERSION: '3.5' + PYTHON_ARCH: '32' + - TOXENV: py35-cover,codecov,coveralls + TOXPYTHON: C:\Python35-x64\python.exe + PYTHON_HOME: C:\Python35-x64 + PYTHON_VERSION: '3.5' + PYTHON_ARCH: '64' + - TOXENV: py35-cover-debug,codecov,coveralls + TOXPYTHON: C:\Python35\python.exe + PYTHON_HOME: C:\Python35 + PYTHON_VERSION: '3.5' + PYTHON_ARCH: '32' + - TOXENV: py35-cover-debug,codecov,coveralls + TOXPYTHON: C:\Python35-x64\python.exe + PYTHON_HOME: C:\Python35-x64 + PYTHON_VERSION: '3.5' + PYTHON_ARCH: '64' + - TOXENV: py35-nocov,codecov,coveralls + TOXPYTHON: C:\Python35\python.exe + PYTHON_HOME: C:\Python35 + PYTHON_VERSION: '3.5' + PYTHON_ARCH: '32' + WHEEL_PATH: .tox/dist + - TOXENV: py35-nocov,codecov,coveralls + TOXPYTHON: C:\Python35-x64\python.exe + PYTHON_HOME: C:\Python35-x64 + PYTHON_VERSION: '3.5' + PYTHON_ARCH: '64' + WHEEL_PATH: .tox/dist + - TOXENV: py35-nocov-debug,codecov,coveralls + TOXPYTHON: C:\Python35\python.exe + PYTHON_HOME: C:\Python35 + PYTHON_VERSION: '3.5' + PYTHON_ARCH: '32' + WHEEL_PATH: .tox/dist + - TOXENV: py35-nocov-debug,codecov,coveralls + TOXPYTHON: C:\Python35-x64\python.exe + PYTHON_HOME: C:\Python35-x64 + PYTHON_VERSION: '3.5' + PYTHON_ARCH: '64' + WHEEL_PATH: .tox/dist + - TOXENV: py36-cover,codecov,coveralls + TOXPYTHON: C:\Python36\python.exe + PYTHON_HOME: C:\Python36 + PYTHON_VERSION: '3.6' + PYTHON_ARCH: '32' + - TOXENV: py36-cover,codecov,coveralls + TOXPYTHON: C:\Python36-x64\python.exe + PYTHON_HOME: C:\Python36-x64 + PYTHON_VERSION: '3.6' + PYTHON_ARCH: '64' + - TOXENV: py36-cover-debug,codecov,coveralls + TOXPYTHON: C:\Python36\python.exe + PYTHON_HOME: C:\Python36 + PYTHON_VERSION: '3.6' + PYTHON_ARCH: '32' + - TOXENV: py36-cover-debug,codecov,coveralls + TOXPYTHON: C:\Python36-x64\python.exe + PYTHON_HOME: C:\Python36-x64 + PYTHON_VERSION: '3.6' + PYTHON_ARCH: '64' + - TOXENV: py36-nocov,codecov,coveralls + TOXPYTHON: C:\Python36\python.exe + PYTHON_HOME: C:\Python36 + PYTHON_VERSION: '3.6' + PYTHON_ARCH: '32' + WHEEL_PATH: .tox/dist + - TOXENV: py36-nocov,codecov,coveralls + TOXPYTHON: C:\Python36-x64\python.exe + PYTHON_HOME: C:\Python36-x64 + PYTHON_VERSION: '3.6' + PYTHON_ARCH: '64' + WHEEL_PATH: .tox/dist + - TOXENV: py36-nocov-debug,codecov,coveralls + TOXPYTHON: C:\Python36\python.exe + PYTHON_HOME: C:\Python36 + PYTHON_VERSION: '3.6' + PYTHON_ARCH: '32' + WHEEL_PATH: .tox/dist + - TOXENV: py36-nocov-debug,codecov,coveralls + TOXPYTHON: C:\Python36-x64\python.exe + PYTHON_HOME: C:\Python36-x64 + PYTHON_VERSION: '3.6' + PYTHON_ARCH: '64' + WHEEL_PATH: .tox/dist + - TOXENV: py37-cover,codecov,coveralls + TOXPYTHON: C:\Python37\python.exe + PYTHON_HOME: C:\Python37 + PYTHON_VERSION: '3.7' + PYTHON_ARCH: '32' + - TOXENV: py37-cover,codecov,coveralls + TOXPYTHON: C:\Python37-x64\python.exe + PYTHON_HOME: C:\Python37-x64 + PYTHON_VERSION: '3.7' + PYTHON_ARCH: '64' + - TOXENV: py37-cover-debug,codecov,coveralls + TOXPYTHON: C:\Python37\python.exe + PYTHON_HOME: C:\Python37 + PYTHON_VERSION: '3.7' + PYTHON_ARCH: '32' + - TOXENV: py37-cover-debug,codecov,coveralls + TOXPYTHON: C:\Python37-x64\python.exe + PYTHON_HOME: C:\Python37-x64 + PYTHON_VERSION: '3.7' + PYTHON_ARCH: '64' + - TOXENV: py37-nocov,codecov,coveralls + TOXPYTHON: C:\Python37\python.exe + PYTHON_HOME: C:\Python37 + PYTHON_VERSION: '3.7' + PYTHON_ARCH: '32' + WHEEL_PATH: .tox/dist + - TOXENV: py37-nocov,codecov,coveralls + TOXPYTHON: C:\Python37-x64\python.exe + PYTHON_HOME: C:\Python37-x64 + PYTHON_VERSION: '3.7' + PYTHON_ARCH: '64' + WHEEL_PATH: .tox/dist + - TOXENV: py37-nocov-debug,codecov,coveralls + TOXPYTHON: C:\Python37\python.exe + PYTHON_HOME: C:\Python37 + PYTHON_VERSION: '3.7' + PYTHON_ARCH: '32' + WHEEL_PATH: .tox/dist + - TOXENV: py37-nocov-debug,codecov,coveralls + TOXPYTHON: C:\Python37-x64\python.exe + PYTHON_HOME: C:\Python37-x64 + PYTHON_VERSION: '3.7' + PYTHON_ARCH: '64' + WHEEL_PATH: .tox/dist + - TOXENV: py38-cover,codecov,coveralls + TOXPYTHON: C:\Python38\python.exe + PYTHON_HOME: C:\Python38 + PYTHON_VERSION: '3.8' + PYTHON_ARCH: '32' + - TOXENV: py38-cover,codecov,coveralls + TOXPYTHON: C:\Python38-x64\python.exe + PYTHON_HOME: C:\Python38-x64 + PYTHON_VERSION: '3.8' + PYTHON_ARCH: '64' + - TOXENV: py38-cover-debug,codecov,coveralls + TOXPYTHON: C:\Python38\python.exe + PYTHON_HOME: C:\Python38 + PYTHON_VERSION: '3.8' + PYTHON_ARCH: '32' + - TOXENV: py38-cover-debug,codecov,coveralls + TOXPYTHON: C:\Python38-x64\python.exe + PYTHON_HOME: C:\Python38-x64 + PYTHON_VERSION: '3.8' + PYTHON_ARCH: '64' + - TOXENV: py38-nocov,codecov,coveralls + TOXPYTHON: C:\Python38\python.exe + PYTHON_HOME: C:\Python38 + PYTHON_VERSION: '3.8' + PYTHON_ARCH: '32' + WHEEL_PATH: .tox/dist + - TOXENV: py38-nocov,codecov,coveralls + TOXPYTHON: C:\Python38-x64\python.exe + PYTHON_HOME: C:\Python38-x64 + PYTHON_VERSION: '3.8' + PYTHON_ARCH: '64' + WHEEL_PATH: .tox/dist + - TOXENV: py38-nocov-debug,codecov,coveralls + TOXPYTHON: C:\Python38\python.exe + PYTHON_HOME: C:\Python38 + PYTHON_VERSION: '3.8' + PYTHON_ARCH: '32' + WHEEL_PATH: .tox/dist + - TOXENV: py38-nocov-debug,codecov,coveralls + TOXPYTHON: C:\Python38-x64\python.exe + PYTHON_HOME: C:\Python38-x64 + PYTHON_VERSION: '3.8' + PYTHON_ARCH: '64' + WHEEL_PATH: .tox/dist +init: + - ps: echo $env:TOXENV + - ps: ls C:\Python* +install: + - '%PYTHON_HOME%\python -mpip install --progress-bar=off tox -rci/requirements.txt' + - '%PYTHON_HOME%\Scripts\virtualenv --version' + - '%PYTHON_HOME%\Scripts\easy_install --version' + - '%PYTHON_HOME%\Scripts\pip --version' + - '%PYTHON_HOME%\Scripts\tox --version' +test_script: + - cmd /E:ON /V:ON /C .\ci\appveyor-with-compiler.cmd %PYTHON_HOME%\Scripts\tox +on_failure: + - ps: dir "env:" + - ps: get-content .tox\*\log\* + +### To enable remote debugging uncomment this (also, see: http://www.appveyor.com/docs/how-to/rdp-to-build-worker): +# on_finish: +# - ps: $blockRdp = $true; iex ((new-object net.webclient).DownloadString('https://raw.githubusercontent.com/appveyor/ci/master/scripts/enable-rdp.ps1')) diff --git a/.bumpversion.cfg b/.bumpversion.cfg index 68c0247..c0b2e4c 100644 --- a/.bumpversion.cfg +++ b/.bumpversion.cfg @@ -4,8 +4,17 @@ commit = True tag = True [bumpversion:file:setup.py] +search = version='{current_version}' +replace = version='{new_version}' + +[bumpversion:file:README.rst] +search = v{current_version}. +replace = v{new_version}. [bumpversion:file:docs/conf.py] +search = version = release = '{current_version}' +replace = version = release = '{new_version}' [bumpversion:file:src/aspectlib/__init__.py] - +search = __version__ = '{current_version}' +replace = __version__ = '{new_version}' diff --git a/.cookiecutterrc b/.cookiecutterrc index 962e811..14607e8 100644 --- a/.cookiecutterrc +++ b/.cookiecutterrc @@ -1,35 +1,56 @@ # Generated by cookiepatcher, a small shim around cookiecutter (pip install cookiepatcher) -cookiecutter: - appveyor: 'yes' - c_extension_cython: 'yes' - c_extension_optional: 'no' - c_extension_support: 'no' - codacy: 'yes' - codeclimate: 'yes' - codecov: 'yes' - command_line_interface: 'no' - command_line_interface_bin_name: aspectlib - coveralls: 'yes' - distribution_name: aspectlib - email: contact@ionelmc.ro - full_name: Ionel Cristian Mărieș - github_username: ionelmc - landscape: 'yes' - package_name: aspectlib - project_name: Aspectlib - project_short_description: '``aspectlib`` is an aspect-oriented programming, monkey-patch - and decorators library. It is useful when changing' - release_date: '2015-06-06' - repo_name: python-aspectlib - requiresio: 'yes' - scrutinizer: 'yes' - sphinx_doctest: 'yes' - sphinx_theme: sphinx-py3doc-enhanced-theme - test_matrix_configurator: 'yes' - test_matrix_separate_coverage: 'no' - test_runner: pytest - travis: 'yes' - version: 1.3.3 - website: http://blog.ionelmc.ro - year: 2014-2016 +!!omap +- cookiecutter: !!omap + - full_name: Ionel Cristian Mărieș + - email: contact@ionelmc.ro + - website: http://blog.ionelmc.ro + - project_name: Aspectlib + - repo_name: python-aspectlib + - repo_hosting: github.com + - repo_hosting_domain: github.com + - repo_username: ionelmc + - package_name: aspectlib + - distribution_name: aspectlib + - project_short_description: '``aspectlib`` is an aspect-oriented programming, monkey-patch and decorators library. It is useful when changing' + - release_date: '2016-05-10' + - year_from: '2014' + - year_to: '2020' + - version: 1.4.2 + - license: BSD 2-Clause License + - c_extension_support: no + - c_extension_optional: no + - c_extension_module: '-' + - c_extension_function: '-' + - c_extension_test_pypi: no + - c_extension_test_pypi_username: ionelmc + - test_matrix_configurator: yes + - test_matrix_separate_coverage: no + - test_runner: pytest + - setup_py_uses_test_runner: no + - setup_py_uses_setuptools_scm: no + - pypi_badge: yes + - pypi_disable_upload: no + - allow_tests_inside_package: no + - linter: flake8 + - command_line_interface: no + - command_line_interface_bin_name: '-' + - coveralls: yes + - coveralls_token: mHSWktPkrw65WSlziG8NnK9m1xj2k1kOQ + - codecov: yes + - landscape: no + - scrutinizer: no + - codacy: no + - codacy_projectid: '-' + - codeclimate: no + - sphinx_docs: yes + - sphinx_theme: sphinx-py3doc-enhanced-theme + - sphinx_doctest: yes + - sphinx_docs_hosting: https://python-aspectlib.readthedocs.io/ + - travis: yes + - travis_osx: no + - appveyor: yes + - requiresio: yes + - _extensions: + - jinja2_time.TimeExtension + - _template: /home/ionel/open-source/cookiecutter-pylibrary diff --git a/.coveragerc b/.coveragerc index 772866d..79a44ff 100644 --- a/.coveragerc +++ b/.coveragerc @@ -1,9 +1,13 @@ [paths] -source = src +source = + src + */site-packages [run] -branch = True -source = src +branch = true +source = + aspectlib + tests parallel = true [report] diff --git a/.editorconfig b/.editorconfig index 4000618..a9c7977 100644 --- a/.editorconfig +++ b/.editorconfig @@ -1,4 +1,4 @@ -# see http://editorconfig.org +# see https://editorconfig.org/ root = true [*] @@ -11,3 +11,6 @@ charset = utf-8 [*.{bat,cmd,ps1}] end_of_line = crlf + +[*.{yml,yaml}] +indent_size = 2 diff --git a/.gitignore b/.gitignore index 9e8fc16..dfe5838 100644 --- a/.gitignore +++ b/.gitignore @@ -1,4 +1,5 @@ *.py[cod] +__pycache__ # C extensions *.so @@ -14,12 +15,14 @@ parts bin var sdist +wheelhouse develop-eggs .installed.cfg lib lib64 venv*/ pyvenv*/ +pip-wheel-metadata/ # Installer logs pip-log.txt @@ -28,6 +31,7 @@ pip-log.txt .coverage .tox .coverage.* +.pytest_cache/ nosetests.xml coverage.xml htmlcov @@ -58,6 +62,10 @@ docs/_build .env .cache .pytest +.benchmarks .bootstrap .appveyor.token *.bak + +# Mypy Cache +.mypy_cache/ diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml new file mode 100644 index 0000000..49285a7 --- /dev/null +++ b/.pre-commit-config.yaml @@ -0,0 +1,19 @@ +# To install the git pre-commit hook run: +# pre-commit install +# To update the pre-commit hooks run: +# pre-commit install-hooks +exclude: '^(.tox|ci/templates)/' +repos: + - repo: https://github.com/pre-commit/pre-commit-hooks + rev: master + hooks: + - id: trailing-whitespace + - id: end-of-file-fixer + - repo: https://github.com/timothycrosley/isort + rev: master + hooks: + - id: isort + - repo: https://gitlab.com/pycqa/flake8 + rev: master + hooks: + - id: flake8 diff --git a/.readthedocs.yml b/.readthedocs.yml new file mode 100644 index 0000000..59ff5c0 --- /dev/null +++ b/.readthedocs.yml @@ -0,0 +1,10 @@ +# See https://docs.readthedocs.io/en/stable/config-file/v2.html for details +version: 2 +sphinx: + configuration: docs/conf.py +formats: all +python: + install: + - requirements: docs/requirements.txt + - method: pip + path: . diff --git a/.travis.yml b/.travis.yml index 10c545c..9a50bbc 100644 --- a/.travis.yml +++ b/.travis.yml @@ -1,40 +1,112 @@ language: python -python: '3.5' -sudo: false +dist: xenial +cache: false env: global: - LD_PRELOAD=/lib/x86_64-linux-gnu/libSegFault.so - SEGFAULT_SIGNALS=all - matrix: - - TOXENV=check - - TOXENV=docs - - - TOXENV=2.7,coveralls,codecov - - TOXENV=2.7-debug,coveralls,codecov - - TOXENV=2.7-nocover - - TOXENV=2.7-nocover-debug - - TOXENV=3.3,coveralls,codecov - - TOXENV=3.3-debug,coveralls,codecov - - TOXENV=3.3-nocover - - TOXENV=3.3-nocover-debug - - TOXENV=3.4,coveralls,codecov - - TOXENV=3.4-debug,coveralls,codecov - - TOXENV=3.4-nocover - - TOXENV=3.4-nocover-debug - - TOXENV=3.5,coveralls,codecov - - TOXENV=3.5-debug,coveralls,codecov - - TOXENV=3.5-nocover - - TOXENV=3.5-nocover-debug - - TOXENV=pypy,coveralls,codecov - - TOXENV=pypy-debug,coveralls,codecov - - TOXENV=pypy-nocover - - TOXENV=pypy-nocover-debug +matrix: + include: + - python: '3.6' + env: + - TOXENV=check + - python: '3.6' + env: + - TOXENV=docs + - env: + - TOXENV=py27-cover,codecov,coveralls + python: '2.7' + - env: + - TOXENV=py27-cover-debug,codecov,coveralls + python: '2.7' + - env: + - TOXENV=py27-nocov + python: '2.7' + - env: + - TOXENV=py27-nocov-debug + python: '2.7' + - env: + - TOXENV=py35-cover,codecov,coveralls + python: '3.5' + - env: + - TOXENV=py35-cover-debug,codecov,coveralls + python: '3.5' + - env: + - TOXENV=py35-nocov + python: '3.5' + - env: + - TOXENV=py35-nocov-debug + python: '3.5' + - env: + - TOXENV=py36-cover,codecov,coveralls + python: '3.6' + - env: + - TOXENV=py36-cover-debug,codecov,coveralls + python: '3.6' + - env: + - TOXENV=py36-nocov + python: '3.6' + - env: + - TOXENV=py36-nocov-debug + python: '3.6' + - env: + - TOXENV=py37-cover,codecov,coveralls + python: '3.7' + - env: + - TOXENV=py37-cover-debug,codecov,coveralls + python: '3.7' + - env: + - TOXENV=py37-nocov + python: '3.7' + - env: + - TOXENV=py37-nocov-debug + python: '3.7' + - env: + - TOXENV=py38-cover,codecov,coveralls + python: '3.8' + - env: + - TOXENV=py38-cover-debug,codecov,coveralls + python: '3.8' + - env: + - TOXENV=py38-nocov + python: '3.8' + - env: + - TOXENV=py38-nocov-debug + python: '3.8' + - env: + - TOXENV=pypy-cover,codecov,coveralls + python: 'pypy' + - env: + - TOXENV=pypy-cover-debug,codecov,coveralls + python: 'pypy' + - env: + - TOXENV=pypy-nocov + python: 'pypy' + - env: + - TOXENV=pypy-nocov-debug + python: 'pypy' + - env: + - TOXENV=pypy3-cover,codecov,coveralls + - TOXPYTHON=pypy3 + python: 'pypy3' + - env: + - TOXENV=pypy3-cover-debug,codecov,coveralls + - TOXPYTHON=pypy3 + python: 'pypy3' + - env: + - TOXENV=pypy3-nocov + - TOXPYTHON=pypy3 + python: 'pypy3' + - env: + - TOXENV=pypy3-nocov-debug + - TOXPYTHON=pypy3 + python: 'pypy3' before_install: - python --version - uname -a - - lsb_release -a + - lsb_release -a || true install: - - pip install tox + - python -mpip install --progress-bar=off tox -rci/requirements.txt - virtualenv --version - easy_install --version - pip --version @@ -44,11 +116,6 @@ script: after_failure: - more .tox/log/* | cat - more .tox/*/log/* | cat -before_cache: - - rm -rf $HOME/.cache/pip/log -cache: - directories: - - $HOME/.cache/pip notifications: email: on_success: never diff --git a/CONTRIBUTING.rst b/CONTRIBUTING.rst index 6c0e46c..9b57832 100644 --- a/CONTRIBUTING.rst +++ b/CONTRIBUTING.rst @@ -41,7 +41,7 @@ To set up `python-aspectlib` for local development: (look for the "Fork" button). 2. Clone your fork locally:: - git clone git@github.com:your_name_here/python-aspectlib.git + git clone git@github.com:ionelmc/python-aspectlib.git 3. Create a branch for local development:: @@ -49,7 +49,7 @@ To set up `python-aspectlib` for local development: Now you can make your changes locally. -4. When you're done making changes, run all the checks, doc builder and spell checker with `tox `_ one command:: +4. When you're done making changes run all the checks and docs builder with `tox `_ one command:: tox @@ -83,7 +83,7 @@ Tips To run a subset of tests:: - tox -e envname -- py.test -k test_myfeature + tox -e envname -- pytest -k test_myfeature To run all the test environments in *parallel* (you need to ``pip install detox``):: diff --git a/LICENSE b/LICENSE index 9ac3fbe..69350bf 100644 --- a/LICENSE +++ b/LICENSE @@ -1,6 +1,6 @@ -Copyright (c) 2014-2016, Ionel Cristian Mărieș -All rights reserved. +BSD 2-Clause License +Copyright (c) 2014-2020, Ionel Cristian Mărieș. All rights reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: diff --git a/MANIFEST.in b/MANIFEST.in index bd48001..942b8c3 100644 --- a/MANIFEST.in +++ b/MANIFEST.in @@ -1,5 +1,4 @@ graft docs -graft examples graft src graft ci graft tests @@ -8,7 +7,6 @@ include .bumpversion.cfg include .coveragerc include .cookiecutterrc include .editorconfig -include .isort.cfg include AUTHORS.rst include CHANGELOG.rst @@ -16,9 +14,7 @@ include CONTRIBUTING.rst include LICENSE include README.rst -include tox.ini .travis.yml appveyor.yml +include tox.ini .travis.yml .appveyor.yml .readthedocs.yml .pre-commit-config.yaml include conftest.py -global-exclude *.py[cod] __pycache__ *.so *.dylib - -prune .idea +global-exclude *.py[cod] __pycache__/* *.so *.dylib diff --git a/README.rst b/README.rst index 51eca7a..f9b6e95 100644 --- a/README.rst +++ b/README.rst @@ -12,15 +12,14 @@ Overview * - tests - | |travis| |appveyor| |requires| | |coveralls| |codecov| - | |landscape| |scrutinizer| |codacy| |codeclimate| * - package - - |version| |downloads| |wheel| |supported-versions| |supported-implementations| - + - | |version| |wheel| |supported-versions| |supported-implementations| + | |commits-since| .. |docs| image:: https://readthedocs.org/projects/python-aspectlib/badge/?style=flat :target: https://readthedocs.org/projects/python-aspectlib :alt: Documentation Status -.. |travis| image:: https://travis-ci.org/ionelmc/python-aspectlib.svg?branch=master +.. |travis| image:: https://api.travis-ci.org/ionelmc/python-aspectlib.svg?branch=master :alt: Travis-CI Build Status :target: https://travis-ci.org/ionelmc/python-aspectlib @@ -36,45 +35,30 @@ Overview :alt: Coverage Status :target: https://coveralls.io/r/ionelmc/python-aspectlib -.. |codecov| image:: https://codecov.io/github/ionelmc/python-aspectlib/coverage.svg?branch=master +.. |codecov| image:: https://codecov.io/gh/ionelmc/python-aspectlib/branch/master/graphs/badge.svg?branch=master :alt: Coverage Status :target: https://codecov.io/github/ionelmc/python-aspectlib -.. |landscape| image:: https://landscape.io/github/ionelmc/python-aspectlib/master/landscape.svg?style=flat - :target: https://landscape.io/github/ionelmc/python-aspectlib/master - :alt: Code Quality Status - -.. |codacy| image:: https://img.shields.io/codacy/9557dc3ca38f43bcac85240f73e1985a.svg?style=flat - :target: https://www.codacy.com/app/ionelmc/python-aspectlib - :alt: Codacy Code Quality Status - -.. |codeclimate| image:: https://codeclimate.com/github/ionelmc/python-aspectlib/badges/gpa.svg - :target: https://codeclimate.com/github/ionelmc/python-aspectlib - :alt: CodeClimate Quality Status - -.. |version| image:: https://img.shields.io/pypi/v/aspectlib.svg?style=flat +.. |version| image:: https://img.shields.io/pypi/v/aspectlib.svg :alt: PyPI Package latest release - :target: https://pypi.python.org/pypi/aspectlib - -.. |downloads| image:: https://img.shields.io/pypi/dm/aspectlib.svg?style=flat - :alt: PyPI Package monthly downloads - :target: https://pypi.python.org/pypi/aspectlib + :target: https://pypi.org/project/aspectlib -.. |wheel| image:: https://img.shields.io/pypi/wheel/aspectlib.svg?style=flat +.. |wheel| image:: https://img.shields.io/pypi/wheel/aspectlib.svg :alt: PyPI Wheel - :target: https://pypi.python.org/pypi/aspectlib + :target: https://pypi.org/project/aspectlib -.. |supported-versions| image:: https://img.shields.io/pypi/pyversions/aspectlib.svg?style=flat +.. |supported-versions| image:: https://img.shields.io/pypi/pyversions/aspectlib.svg :alt: Supported versions - :target: https://pypi.python.org/pypi/aspectlib + :target: https://pypi.org/project/aspectlib -.. |supported-implementations| image:: https://img.shields.io/pypi/implementation/aspectlib.svg?style=flat +.. |supported-implementations| image:: https://img.shields.io/pypi/implementation/aspectlib.svg :alt: Supported implementations - :target: https://pypi.python.org/pypi/aspectlib + :target: https://pypi.org/project/aspectlib + +.. |commits-since| image:: https://img.shields.io/github/commits-since/ionelmc/python-aspectlib/v1.4.2.svg + :alt: Commits since latest release + :target: https://github.com/ionelmc/python-aspectlib/compare/v1.4.2...master -.. |scrutinizer| image:: https://img.shields.io/scrutinizer/g/ionelmc/python-aspectlib/master.svg?style=flat - :alt: Scrutinizer Status - :target: https://scrutinizer-ci.com/g/ionelmc/python-aspectlib/ .. end-badges diff --git a/appveyor.yml b/appveyor.yml deleted file mode 100644 index 8e6f61d..0000000 --- a/appveyor.yml +++ /dev/null @@ -1,238 +0,0 @@ -version: '{branch}-{build}' -build: off -cache: - - '%LOCALAPPDATA%\pip\Cache' -environment: - global: - WITH_COMPILER: 'cmd /E:ON /V:ON /C .\ci\appveyor-with-compiler.cmd' - matrix: - - TOXENV: check - PYTHON_HOME: C:\Python27 - PYTHON_VERSION: '2.7' - PYTHON_ARCH: '32' - - - TOXENV: '2.7,codecov' - TOXPYTHON: C:\Python27\python.exe - PYTHON_HOME: C:\Python27 - PYTHON_VERSION: '2.7' - PYTHON_ARCH: '32' - - - TOXENV: '2.7,codecov' - TOXPYTHON: C:\Python27-x64\python.exe - WINDOWS_SDK_VERSION: v7.0 - PYTHON_HOME: C:\Python27-x64 - PYTHON_VERSION: '2.7' - PYTHON_ARCH: '64' - - - TOXENV: '2.7-debug,codecov' - TOXPYTHON: C:\Python27\python.exe - PYTHON_HOME: C:\Python27 - PYTHON_VERSION: '2.7' - PYTHON_ARCH: '32' - - - TOXENV: '2.7-debug,codecov' - TOXPYTHON: C:\Python27-x64\python.exe - WINDOWS_SDK_VERSION: v7.0 - PYTHON_HOME: C:\Python27-x64 - PYTHON_VERSION: '2.7' - PYTHON_ARCH: '64' - - - TOXENV: '2.7-nocover' - TOXPYTHON: C:\Python27\python.exe - PYTHON_HOME: C:\Python27 - PYTHON_VERSION: '2.7' - PYTHON_ARCH: '32' - - - TOXENV: '2.7-nocover' - TOXPYTHON: C:\Python27-x64\python.exe - WINDOWS_SDK_VERSION: v7.0 - PYTHON_HOME: C:\Python27-x64 - PYTHON_VERSION: '2.7' - PYTHON_ARCH: '64' - - - TOXENV: '2.7-nocover-debug' - TOXPYTHON: C:\Python27\python.exe - PYTHON_HOME: C:\Python27 - PYTHON_VERSION: '2.7' - PYTHON_ARCH: '32' - - - TOXENV: '2.7-nocover-debug' - TOXPYTHON: C:\Python27-x64\python.exe - WINDOWS_SDK_VERSION: v7.0 - PYTHON_HOME: C:\Python27-x64 - PYTHON_VERSION: '2.7' - PYTHON_ARCH: '64' - - - TOXENV: '3.3,codecov' - TOXPYTHON: C:\Python33\python.exe - PYTHON_HOME: C:\Python33 - PYTHON_VERSION: '3.3' - PYTHON_ARCH: '32' - - - TOXENV: '3.3,codecov' - TOXPYTHON: C:\Python33-x64\python.exe - WINDOWS_SDK_VERSION: v7.1 - PYTHON_HOME: C:\Python33-x64 - PYTHON_VERSION: '3.3' - PYTHON_ARCH: '64' - - - TOXENV: '3.3-debug,codecov' - TOXPYTHON: C:\Python33\python.exe - PYTHON_HOME: C:\Python33 - PYTHON_VERSION: '3.3' - PYTHON_ARCH: '32' - - - TOXENV: '3.3-debug,codecov' - TOXPYTHON: C:\Python33-x64\python.exe - WINDOWS_SDK_VERSION: v7.1 - PYTHON_HOME: C:\Python33-x64 - PYTHON_VERSION: '3.3' - PYTHON_ARCH: '64' - - - TOXENV: '3.3-nocover' - TOXPYTHON: C:\Python33\python.exe - PYTHON_HOME: C:\Python33 - PYTHON_VERSION: '3.3' - PYTHON_ARCH: '32' - - - TOXENV: '3.3-nocover' - TOXPYTHON: C:\Python33-x64\python.exe - WINDOWS_SDK_VERSION: v7.1 - PYTHON_HOME: C:\Python33-x64 - PYTHON_VERSION: '3.3' - PYTHON_ARCH: '64' - - - TOXENV: '3.3-nocover-debug' - TOXPYTHON: C:\Python33\python.exe - PYTHON_HOME: C:\Python33 - PYTHON_VERSION: '3.3' - PYTHON_ARCH: '32' - - - TOXENV: '3.3-nocover-debug' - TOXPYTHON: C:\Python33-x64\python.exe - WINDOWS_SDK_VERSION: v7.1 - PYTHON_HOME: C:\Python33-x64 - PYTHON_VERSION: '3.3' - PYTHON_ARCH: '64' - - - TOXENV: '3.4,codecov' - TOXPYTHON: C:\Python34\python.exe - PYTHON_HOME: C:\Python34 - PYTHON_VERSION: '3.4' - PYTHON_ARCH: '32' - - - TOXENV: '3.4,codecov' - TOXPYTHON: C:\Python34-x64\python.exe - WINDOWS_SDK_VERSION: v7.1 - PYTHON_HOME: C:\Python34-x64 - PYTHON_VERSION: '3.4' - PYTHON_ARCH: '64' - - - TOXENV: '3.4-debug,codecov' - TOXPYTHON: C:\Python34\python.exe - PYTHON_HOME: C:\Python34 - PYTHON_VERSION: '3.4' - PYTHON_ARCH: '32' - - - TOXENV: '3.4-debug,codecov' - TOXPYTHON: C:\Python34-x64\python.exe - WINDOWS_SDK_VERSION: v7.1 - PYTHON_HOME: C:\Python34-x64 - PYTHON_VERSION: '3.4' - PYTHON_ARCH: '64' - - - TOXENV: '3.4-nocover' - TOXPYTHON: C:\Python34\python.exe - PYTHON_HOME: C:\Python34 - PYTHON_VERSION: '3.4' - PYTHON_ARCH: '32' - - - TOXENV: '3.4-nocover' - TOXPYTHON: C:\Python34-x64\python.exe - WINDOWS_SDK_VERSION: v7.1 - PYTHON_HOME: C:\Python34-x64 - PYTHON_VERSION: '3.4' - PYTHON_ARCH: '64' - - - TOXENV: '3.4-nocover-debug' - TOXPYTHON: C:\Python34\python.exe - PYTHON_HOME: C:\Python34 - PYTHON_VERSION: '3.4' - PYTHON_ARCH: '32' - - - TOXENV: '3.4-nocover-debug' - TOXPYTHON: C:\Python34-x64\python.exe - WINDOWS_SDK_VERSION: v7.1 - PYTHON_HOME: C:\Python34-x64 - PYTHON_VERSION: '3.4' - PYTHON_ARCH: '64' - - - TOXENV: '3.5,codecov' - TOXPYTHON: C:\Python35\python.exe - PYTHON_HOME: C:\Python35 - PYTHON_VERSION: '3.5' - PYTHON_ARCH: '32' - - - TOXENV: '3.5,codecov' - TOXPYTHON: C:\Python35-x64\python.exe - PYTHON_HOME: C:\Python35-x64 - PYTHON_VERSION: '3.5' - PYTHON_ARCH: '64' - - - TOXENV: '3.5-debug,codecov' - TOXPYTHON: C:\Python35\python.exe - PYTHON_HOME: C:\Python35 - PYTHON_VERSION: '3.5' - PYTHON_ARCH: '32' - - - TOXENV: '3.5-debug,codecov' - TOXPYTHON: C:\Python35-x64\python.exe - PYTHON_HOME: C:\Python35-x64 - PYTHON_VERSION: '3.5' - PYTHON_ARCH: '64' - - - TOXENV: '3.5-nocover' - TOXPYTHON: C:\Python35\python.exe - PYTHON_HOME: C:\Python35 - PYTHON_VERSION: '3.5' - PYTHON_ARCH: '32' - - - TOXENV: '3.5-nocover' - TOXPYTHON: C:\Python35-x64\python.exe - PYTHON_HOME: C:\Python35-x64 - PYTHON_VERSION: '3.5' - PYTHON_ARCH: '64' - - - TOXENV: '3.5-nocover-debug' - TOXPYTHON: C:\Python35\python.exe - PYTHON_HOME: C:\Python35 - PYTHON_VERSION: '3.5' - PYTHON_ARCH: '32' - - - TOXENV: '3.5-nocover-debug' - TOXPYTHON: C:\Python35-x64\python.exe - PYTHON_HOME: C:\Python35-x64 - PYTHON_VERSION: '3.5' - PYTHON_ARCH: '64' - -init: - - ps: echo $env:TOXENV - - ps: ls C:\Python* -install: - - python -u ci\appveyor-bootstrap.py - - '%PYTHON_HOME%\Scripts\virtualenv --version' - - '%PYTHON_HOME%\Scripts\easy_install --version' - - '%PYTHON_HOME%\Scripts\pip --version' - - '%PYTHON_HOME%\Scripts\tox --version' -test_script: - - '%WITH_COMPILER% %PYTHON_HOME%\Scripts\tox' - -on_failure: - - ps: dir "env:" - - ps: get-content .tox\*\log\* -artifacts: - - path: dist\* - -### To enable remote debugging uncomment this (also, see: http://www.appveyor.com/docs/how-to/rdp-to-build-worker): -# on_finish: -# - ps: $blockRdp = $true; iex ((new-object net.webclient).DownloadString('https://raw.githubusercontent.com/appveyor/ci/master/scripts/enable-rdp.ps1')) diff --git a/ci/appveyor-bootstrap.py b/ci/appveyor-bootstrap.py deleted file mode 100644 index 74abd0b..0000000 --- a/ci/appveyor-bootstrap.py +++ /dev/null @@ -1,113 +0,0 @@ -""" -AppVeyor will at least have few Pythons around so there's no point of implementing a bootstrapper in PowerShell. - -This is a port of https://github.com/pypa/python-packaging-user-guide/blob/master/source/code/install.ps1 -with various fixes and improvements that just weren't feasible to implement in PowerShell. -""" -from __future__ import print_function -from os import environ -from os.path import exists -from subprocess import check_call - -try: - from urllib.request import urlretrieve -except ImportError: - from urllib import urlretrieve - -BASE_URL = "https://www.python.org/ftp/python/" -GET_PIP_URL = "https://bootstrap.pypa.io/get-pip.py" -GET_PIP_PATH = "C:\get-pip.py" -URLS = { - ("2.7", "64"): BASE_URL + "2.7.10/python-2.7.10.amd64.msi", - ("2.7", "32"): BASE_URL + "2.7.10/python-2.7.10.msi", - # NOTE: no .msi installer for 3.3.6 - ("3.3", "64"): BASE_URL + "3.3.3/python-3.3.3.amd64.msi", - ("3.3", "32"): BASE_URL + "3.3.3/python-3.3.3.msi", - ("3.4", "64"): BASE_URL + "3.4.3/python-3.4.3.amd64.msi", - ("3.4", "32"): BASE_URL + "3.4.3/python-3.4.3.msi", - ("3.5", "64"): BASE_URL + "3.5.0/python-3.5.0-amd64.exe", - ("3.5", "32"): BASE_URL + "3.5.0/python-3.5.0.exe", -} -INSTALL_CMD = { - # Commands are allowed to fail only if they are not the last command. Eg: uninstall (/x) allowed to fail. - "2.7": [["msiexec.exe", "/L*+!", "install.log", "/qn", "/x", "{path}"], - ["msiexec.exe", "/L*+!", "install.log", "/qn", "/i", "{path}", "TARGETDIR={home}"]], - "3.3": [["msiexec.exe", "/L*+!", "install.log", "/qn", "/x", "{path}"], - ["msiexec.exe", "/L*+!", "install.log", "/qn", "/i", "{path}", "TARGETDIR={home}"]], - "3.4": [["msiexec.exe", "/L*+!", "install.log", "/qn", "/x", "{path}"], - ["msiexec.exe", "/L*+!", "install.log", "/qn", "/i", "{path}", "TARGETDIR={home}"]], - "3.5": [["{path}", "/quiet", "TargetDir={home}"]], -} - - -def download_file(url, path): - print("Downloading: {} (into {})".format(url, path)) - progress = [0, 0] - - def report(count, size, total): - progress[0] = count * size - if progress[0] - progress[1] > 1000000: - progress[1] = progress[0] - print("Downloaded {:,}/{:,} ...".format(progress[1], total)) - - dest, _ = urlretrieve(url, path, reporthook=report) - return dest - - -def install_python(version, arch, home): - print("Installing Python", version, "for", arch, "bit architecture to", home) - if exists(home): - return - - path = download_python(version, arch) - print("Installing", path, "to", home) - success = False - for cmd in INSTALL_CMD[version]: - cmd = [part.format(home=home, path=path) for part in cmd] - print("Running:", " ".join(cmd)) - try: - check_call(cmd) - except Exception as exc: - print("Failed command", cmd, "with:", exc) - if exists("install.log"): - with open("install.log") as fh: - print(fh.read()) - else: - success = True - if success: - print("Installation complete!") - else: - print("Installation failed") - - -def download_python(version, arch): - for _ in range(3): - try: - return download_file(URLS[version, arch], "installer.exe") - except Exception as exc: - print("Failed to download:", exc) - print("Retrying ...") - - -def install_pip(home): - pip_path = home + "/Scripts/pip.exe" - python_path = home + "/python.exe" - if exists(pip_path): - print("pip already installed.") - else: - print("Installing pip...") - download_file(GET_PIP_URL, GET_PIP_PATH) - print("Executing:", python_path, GET_PIP_PATH) - check_call([python_path, GET_PIP_PATH]) - - -def install_packages(home, *packages): - cmd = [home + "/Scripts/pip.exe", "install"] - cmd.extend(packages) - check_call(cmd) - - -if __name__ == "__main__": - install_python(environ['PYTHON_VERSION'], environ['PYTHON_ARCH'], environ['PYTHON_HOME']) - install_pip(environ['PYTHON_HOME']) - install_packages(environ['PYTHON_HOME'], "setuptools>=18.0.1", "wheel", "tox", "virtualenv>=13.1.0") diff --git a/ci/appveyor-with-compiler.cmd b/ci/appveyor-with-compiler.cmd index 7f82a02..289585f 100644 --- a/ci/appveyor-with-compiler.cmd +++ b/ci/appveyor-with-compiler.cmd @@ -1,39 +1,17 @@ -:: To build extensions for 64 bit Python 3, we need to configure environment -:: variables to use the MSVC 2010 C++ compilers from GRMSDKX_EN_DVD.iso of: -:: MS Windows SDK for Windows 7 and .NET Framework 4 (SDK v7.1) -:: -:: To build extensions for 64 bit Python 2, we need to configure environment -:: variables to use the MSVC 2008 C++ compilers from GRMSDKX_EN_DVD.iso of: -:: MS Windows SDK for Windows 7 and .NET Framework 3.5 (SDK v7.0) -:: -:: 32 bit builds do not require specific environment configurations. -:: -:: Note: this script needs to be run with the /E:ON and /V:ON flags for the -:: cmd interpreter, at least for (SDK v7.0) -:: -:: More details at: -:: https://github.com/cython/cython/wiki/64BitCythonExtensionsOnWindows -:: http://stackoverflow.com/a/13751649/163740 -:: -:: Author: Olivier Grisel -:: License: CC0 1.0 Universal: http://creativecommons.org/publicdomain/zero/1.0/ +:: Very simple setup: +:: - if WINDOWS_SDK_VERSION is set then activate the SDK. +:: - disable the WDK if it's around. + SET COMMAND_TO_RUN=%* SET WIN_SDK_ROOT=C:\Program Files\Microsoft SDKs\Windows SET WIN_WDK="c:\Program Files (x86)\Windows Kits\10\Include\wdf" ECHO SDK: %WINDOWS_SDK_VERSION% ARCH: %PYTHON_ARCH% - -IF "%PYTHON_VERSION%"=="3.5" ( - IF EXIST %WIN_WDK% ( - REM See: https://connect.microsoft.com/VisualStudio/feedback/details/1610302/ - REN %WIN_WDK% 0wdf - ) - GOTO main -) - -IF "%PYTHON_ARCH%"=="32" ( - GOTO main +IF EXIST %WIN_WDK% ( + REM See: https://connect.microsoft.com/VisualStudio/feedback/details/1610302/ + REN %WIN_WDK% 0wdf ) +IF "%WINDOWS_SDK_VERSION%"=="" GOTO main SET DISTUTILS_USE_SDK=1 SET MSSdk=1 @@ -41,6 +19,5 @@ SET MSSdk=1 CALL "%WIN_SDK_ROOT%\%WINDOWS_SDK_VERSION%\Bin\SetEnv.cmd" /x64 /release :main - ECHO Executing: %COMMAND_TO_RUN% CALL %COMMAND_TO_RUN% || EXIT 1 diff --git a/ci/bootstrap.py b/ci/bootstrap.py index fdfbaf6..7e42276 100755 --- a/ci/bootstrap.py +++ b/ci/bootstrap.py @@ -1,18 +1,26 @@ #!/usr/bin/env python # -*- coding: utf-8 -*- -from __future__ import absolute_import, print_function, unicode_literals +from __future__ import absolute_import +from __future__ import print_function +from __future__ import unicode_literals import os +import subprocess import sys +from os.path import abspath +from os.path import dirname from os.path import exists from os.path import join -from os.path import dirname -from os.path import abspath +base_path = dirname(dirname(abspath(__file__))) -if __name__ == "__main__": - base_path = dirname(dirname(abspath(__file__))) - print("Project path: {0}".format(base_path)) + +def check_call(args): + print("+", *args) + subprocess.check_call(args) + + +def exec_in_env(): env_path = join(base_path, ".tox", "bootstrap") if sys.platform == "win32": bin_path = join(env_path, "Scripts") @@ -20,20 +28,31 @@ bin_path = join(env_path, "bin") if not exists(env_path): import subprocess + print("Making bootstrap env in: {0} ...".format(env_path)) try: - subprocess.check_call(["virtualenv", env_path]) - except Exception: - subprocess.check_call([sys.executable, "-m", "virtualenv", env_path]) - print("Installing `jinja2` and `matrix` into bootstrap environment ...") - subprocess.check_call([join(bin_path, "pip"), "install", "jinja2", "matrix"]) - activate = join(bin_path, "activate_this.py") - exec(compile(open(activate, "rb").read(), activate, "exec"), dict(__file__=activate)) + check_call([sys.executable, "-m", "venv", env_path]) + except subprocess.CalledProcessError: + try: + check_call([sys.executable, "-m", "virtualenv", env_path]) + except subprocess.CalledProcessError: + check_call(["virtualenv", env_path]) + print("Installing `jinja2` into bootstrap environment...") + check_call([join(bin_path, "pip"), "install", "jinja2", "tox", "matrix"]) + python_executable = join(bin_path, "python") + if not os.path.exists(python_executable): + python_executable += '.exe' + + print("Re-executing with: {0}".format(python_executable)) + print("+ exec", python_executable, __file__, "--no-env") + os.execv(python_executable, [python_executable, __file__, "--no-env"]) - import jinja2 +def main(): + import jinja2 import matrix + print("Project path: {0}".format(base_path)) jinja = jinja2.Environment( loader=jinja2.FileSystemLoader(join(base_path, "ci", "templates")), @@ -44,25 +63,30 @@ tox_environments = {} for (alias, conf) in matrix.from_file(join(base_path, "setup.cfg")).items(): - python = conf["python_versions"] deps = conf["dependencies"] - if "coverage_flags" in conf: - cover = {"false": False, "true": True}[conf["coverage_flags"].lower()] - if "environment_variables" in conf: - env_vars = conf["environment_variables"] - tox_environments[alias] = { - "python": "python" + python if "py" not in python else python, "deps": deps.split(), } if "coverage_flags" in conf: + cover = {"false": False, "true": True}[conf["coverage_flags"].lower()] tox_environments[alias].update(cover=cover) if "environment_variables" in conf: + env_vars = conf["environment_variables"] tox_environments[alias].update(env_vars=env_vars.split()) - for name in os.listdir(join("ci", "templates")): with open(join(base_path, name), "w") as fh: fh.write(jinja.get_template(name).render(tox_environments=tox_environments)) print("Wrote {}".format(name)) print("DONE.") + + +if __name__ == "__main__": + args = sys.argv[1:] + if args == ["--no-env"]: + main() + elif not args: + exec_in_env() + else: + print("Unexpected arguments {0}".format(args), file=sys.stderr) + sys.exit(1) diff --git a/ci/requirements.txt b/ci/requirements.txt new file mode 100644 index 0000000..b2a21e5 --- /dev/null +++ b/ci/requirements.txt @@ -0,0 +1,4 @@ +virtualenv>=16.6.0 +pip>=19.1.1 +setuptools>=18.0.1 +six>=1.12.0 diff --git a/ci/templates/.appveyor.yml b/ci/templates/.appveyor.yml new file mode 100644 index 0000000..4c6502e --- /dev/null +++ b/ci/templates/.appveyor.yml @@ -0,0 +1,52 @@ +version: '{branch}-{build}' +build: off +environment: + global: + COVERALLS_EXTRAS: '-v' + COVERALLS_REPO_TOKEN: mHSWktPkrw65WSlziG8NnK9m1xj2k1kOQ + matrix: + - TOXENV: check + TOXPYTHON: C:\Python36\python.exe + PYTHON_HOME: C:\Python36 + PYTHON_VERSION: '3.6' + PYTHON_ARCH: '32' +{% for env, config in tox_environments|dictsort %} +{% if env.startswith(('py2', 'py3')) %} + - TOXENV: {{ env }},codecov,coveralls{{ "" }} + TOXPYTHON: C:\Python{{ env[2:4] }}\python.exe + PYTHON_HOME: C:\Python{{ env[2:4] }} + PYTHON_VERSION: '{{ env[2] }}.{{ env[3] }}' + PYTHON_ARCH: '32' +{% if 'nocov' in env %} + WHEEL_PATH: .tox/dist +{% endif %} + - TOXENV: {{ env }},codecov,coveralls{{ "" }} + TOXPYTHON: C:\Python{{ env[2:4] }}-x64\python.exe + PYTHON_HOME: C:\Python{{ env[2:4] }}-x64 + PYTHON_VERSION: '{{ env[2] }}.{{ env[3] }}' + PYTHON_ARCH: '64' +{% if 'nocov' in env %} + WHEEL_PATH: .tox/dist +{% endif %} +{% if env.startswith('py2') %} + WINDOWS_SDK_VERSION: v7.0 +{% endif %} +{% endif %}{% endfor %} +init: + - ps: echo $env:TOXENV + - ps: ls C:\Python* +install: + - '%PYTHON_HOME%\python -mpip install --progress-bar=off tox -rci/requirements.txt' + - '%PYTHON_HOME%\Scripts\virtualenv --version' + - '%PYTHON_HOME%\Scripts\easy_install --version' + - '%PYTHON_HOME%\Scripts\pip --version' + - '%PYTHON_HOME%\Scripts\tox --version' +test_script: + - cmd /E:ON /V:ON /C .\ci\appveyor-with-compiler.cmd %PYTHON_HOME%\Scripts\tox +on_failure: + - ps: dir "env:" + - ps: get-content .tox\*\log\* + +### To enable remote debugging uncomment this (also, see: http://www.appveyor.com/docs/how-to/rdp-to-build-worker): +# on_finish: +# - ps: $blockRdp = $true; iex ((new-object net.webclient).DownloadString('https://raw.githubusercontent.com/appveyor/ci/master/scripts/enable-rdp.ps1')) diff --git a/ci/templates/.travis.yml b/ci/templates/.travis.yml index a7f4187..84171cd 100644 --- a/ci/templates/.travis.yml +++ b/ci/templates/.travis.yml @@ -1,23 +1,36 @@ language: python -python: '3.5' -sudo: false +dist: xenial +cache: false env: global: - LD_PRELOAD=/lib/x86_64-linux-gnu/libSegFault.so - SEGFAULT_SIGNALS=all - matrix: - - TOXENV=check - - TOXENV=docs -{% for env, config in tox_environments|dictsort %}{{ '' }} - - TOXENV={{ env }}{% if config.cover %},coveralls,codecov{% endif -%} -{% endfor %} - +matrix: + include: + - python: '3.6' + env: + - TOXENV=check + - python: '3.6' + env: + - TOXENV=docs +{%- for env, config in tox_environments|dictsort %}{{ '' }} + - env: + - TOXENV={{ env }}{% if config.cover %},codecov,coveralls{% endif %} +{%- if env.startswith('pypy3') %}{{ '' }} + - TOXPYTHON=pypy3 + python: 'pypy3' +{%- elif env.startswith('pypy') %}{{ '' }} + python: 'pypy' +{%- else %}{{ '' }} + python: '{{ '{0[2]}.{0[3]}'.format(env) }}' +{%- endif %} +{%- endfor %}{{ '' }} before_install: - python --version - uname -a - - lsb_release -a + - lsb_release -a || true install: - - pip install tox + - python -mpip install --progress-bar=off tox -rci/requirements.txt - virtualenv --version - easy_install --version - pip --version @@ -27,11 +40,6 @@ script: after_failure: - more .tox/log/* | cat - more .tox/*/log/* | cat -before_cache: - - rm -rf $HOME/.cache/pip/log -cache: - directories: - - $HOME/.cache/pip notifications: email: on_success: never diff --git a/ci/templates/appveyor.yml b/ci/templates/appveyor.yml deleted file mode 100644 index 4695f1a..0000000 --- a/ci/templates/appveyor.yml +++ /dev/null @@ -1,53 +0,0 @@ -version: '{branch}-{build}' -build: off -cache: - - '%LOCALAPPDATA%\pip\Cache' -environment: - global: - WITH_COMPILER: 'cmd /E:ON /V:ON /C .\ci\appveyor-with-compiler.cmd' - matrix: - - TOXENV: check - PYTHON_HOME: C:\Python27 - PYTHON_VERSION: '2.7' - PYTHON_ARCH: '32' - -{% for env, config in tox_environments|dictsort %}{% if config.python in ('python2.7', 'python3.3', 'python3.4', 'python3.5') %} - - TOXENV: '{{ env }}{% if config.cover %},codecov{% endif %}' - TOXPYTHON: C:\{{ config.python.replace('.', '').capitalize() }}\python.exe - PYTHON_HOME: C:\{{ config.python.replace('.', '').capitalize() }} - PYTHON_VERSION: '{{ config.python[-3:] }}' - PYTHON_ARCH: '32' - - - TOXENV: '{{ env }}{% if config.cover %},codecov{% endif %}' - TOXPYTHON: C:\{{ config.python.replace('.', '').capitalize() }}-x64\python.exe - {%- if config.python != 'python3.5' %} - - WINDOWS_SDK_VERSION: v7.{{ '1' if config.python[-3] == '3' else '0' }} - {%- endif %} - - PYTHON_HOME: C:\{{ config.python.replace('.', '').capitalize() }}-x64 - PYTHON_VERSION: '{{ config.python[-3:] }}' - PYTHON_ARCH: '64' - -{% endif %}{% endfor %} -init: - - ps: echo $env:TOXENV - - ps: ls C:\Python* -install: - - python -u ci\appveyor-bootstrap.py - - '%PYTHON_HOME%\Scripts\virtualenv --version' - - '%PYTHON_HOME%\Scripts\easy_install --version' - - '%PYTHON_HOME%\Scripts\pip --version' - - '%PYTHON_HOME%\Scripts\tox --version' -test_script: - - '%WITH_COMPILER% %PYTHON_HOME%\Scripts\tox' - -on_failure: - - ps: dir "env:" - - ps: get-content .tox\*\log\* -artifacts: - - path: dist\* - -### To enable remote debugging uncomment this (also, see: http://www.appveyor.com/docs/how-to/rdp-to-build-worker): -# on_finish: -# - ps: $blockRdp = $true; iex ((new-object net.webclient).DownloadString('https://raw.githubusercontent.com/appveyor/ci/master/scripts/enable-rdp.ps1')) diff --git a/ci/templates/tox.ini b/ci/templates/tox.ini index ab65608..8ab015e 100644 --- a/ci/templates/tox.ini +++ b/ci/templates/tox.ini @@ -2,16 +2,16 @@ envlist = clean, check, + docs, {% for env in tox_environments|sort %} {{ env }}, {% endfor %} - report, - docs + report [testenv] basepython = - {docs,spell}: python2.7 - {clean,check,report,extension-coveralls,coveralls,codecov}: python3.5 + docs: {env:TOXPYTHON:python3.6} + {bootstrap,clean,check,report,codecov,coveralls}: {env:TOXPYTHON:python3} setenv = PYTHONPATH={toxinidir}/tests PYTHONUNBUFFERED=yes @@ -25,39 +25,18 @@ deps = nose mock tornado - profilestats -commands = - {posargs:py.test -vv --ignore=src} - -[testenv:spell] -setenv = - SPELLCHECK=1 -commands = - sphinx-build -b spelling docs dist/docs -skip_install = true -usedevelop = false -deps = - -r{toxinidir}/docs/requirements.txt - sphinxcontrib-spelling - pyenchant - -[testenv:docs] -deps = - -r{toxinidir}/docs/requirements.txt + pytest-cov + pytest-clarity commands = - sphinx-build {posargs:-E} -b html docs dist/docs - sphinx-build -b linkcheck docs dist/docs + {posargs:pytest --cov --cov-report=term-missing -vv --ignore=src} [testenv:bootstrap] deps = jinja2 matrix skip_install = true -usedevelop = false commands = - python ci/bootstrap.py -passenv = - * + python ci/bootstrap.py --no-env [testenv:check] deps = @@ -68,53 +47,53 @@ deps = pygments isort skip_install = true -usedevelop = false commands = python setup.py check --strict --metadata --restructuredtext check-manifest {toxinidir} - flake8 src tests setup.py - isort --verbose --check-only --diff --recursive src tests setup.py + flake8 + isort --verbose --check-only --diff --recursive + + +[testenv:docs] +usedevelop = true +deps = + -r{toxinidir}/docs/requirements.txt +commands = + sphinx-build {posargs:-E} -b doctest docs dist/docs + sphinx-build {posargs:-E} -b html docs dist/docs + sphinx-build -b linkcheck docs dist/docs [testenv:coveralls] deps = coveralls skip_install = true -usedevelop = false commands = - coverage combine - coverage report coveralls [] + + [testenv:codecov] deps = codecov skip_install = true -usedevelop = false commands = - coverage combine - coverage report - coverage xml --ignore-errors codecov [] - [testenv:report] deps = coverage skip_install = true -usedevelop = false commands = - coverage combine coverage report coverage html [testenv:clean] commands = coverage erase skip_install = true -usedevelop = false deps = coverage - {% for env, config in tox_environments|dictsort %} + [testenv:{{ env }}] -basepython = {env:TOXPYTHON:{{ config.python }}} +basepython = {env:TOXPYTHON:{{ env.split("-")[0] if env.startswith("pypy") else "python{0[2]}.{0[3]}".format(env) }}} {% if config.cover or config.env_vars %} setenv = {[testenv]setenv} @@ -123,10 +102,9 @@ setenv = {{ var }} {% endfor %} {% if config.cover %} - WITH_COVERAGE=yes usedevelop = true commands = - {posargs:py.test --cov --cov-report=term-missing -vv} + {posargs:pytest --cov --cov-report=term-missing -vv} {% endif %} {% if config.cover or config.deps %} deps = @@ -137,8 +115,5 @@ deps = {% endif %} {% for dep in config.deps %} {{ dep }} -{% endfor %} - -{% endfor %} - - +{% endfor -%} +{% endfor -%} diff --git a/conftest.py b/conftest.py index 68e639f..e237da2 100644 --- a/conftest.py +++ b/conftest.py @@ -1,6 +1,8 @@ import sys + PY3 = sys.version_info[0] >= 3 + def pytest_ignore_collect(path, config): basename = path.basename diff --git a/docs/conf.py b/docs/conf.py index 8770587..929671b 100644 --- a/docs/conf.py +++ b/docs/conf.py @@ -1,8 +1,7 @@ # -*- coding: utf-8 -*- from __future__ import unicode_literals -import os - +import sphinx_py3doc_enhanced_theme extensions = [ 'sphinx.ext.autodoc', @@ -15,15 +14,10 @@ 'sphinx.ext.todo', 'sphinx.ext.viewcode', ] -if os.getenv('SPELLCHECK'): - extensions += 'sphinxcontrib.spelling', - spelling_show_suggestions = True - spelling_lang = 'en_US' - source_suffix = '.rst' master_doc = 'index' project = 'Aspectlib' -year = '2014-2016' +year = '2014-2020' author = 'Ionel Cristian Mărieș' copyright = '{0}, {1}'.format(year, author) version = release = '1.4.2' @@ -34,7 +28,6 @@ 'issue': ('https://github.com/ionelmc/python-aspectlib/issues/%s', '#'), 'pr': ('https://github.com/ionelmc/python-aspectlib/pull/%s', 'PR #'), } -import sphinx_py3doc_enhanced_theme html_theme = "sphinx_py3doc_enhanced_theme" html_theme_path = [sphinx_py3doc_enhanced_theme.get_html_theme_path()] html_theme_options = { diff --git a/setup.cfg b/setup.cfg index 09af5e3..c2e0b21 100644 --- a/setup.cfg +++ b/setup.cfg @@ -3,23 +3,22 @@ universal = 1 [flake8] max-line-length = 140 -exclude = tests/*,*/migrations/*,*/south_migrations/* +exclude = .tox ci/templates -[pytest] +[tool:pytest] +# If a pytest section is found in one of the possible config files +# (pytest.ini, tox.ini or setup.cfg), then pytest will not look for any others, +# so if you add a pytest config section elsewhere, +# you will need to delete this section from setup.cfg. norecursedirs = - .git - .tox - .env - dist - build - south_migrations migrations + python_files = test_*.py *_test.py tests.py addopts = - -rxEfsw + -ra --strict --ignore=docs/conf.py --ignore=setup.py @@ -28,19 +27,21 @@ addopts = --doctest-modules --doctest-glob=\*.rst --tb=short +testpaths = + tests -[isort] -force_single_line=True -line_length=120 -known_first_party=aspectlib -default_section=THIRDPARTY -forced_separate=test_aspectlib +[tool:isort] +force_single_line = True +line_length = 120 +known_first_party = aspectlib +default_section = THIRDPARTY +forced_separate = test_aspectlib not_skip = __init__.py -skip = migrations, south_migrations +skip = .tox ci/templates [matrix] # This is the configuration for the `./bootstrap.py` script. -# It generates `.travis.yml`, `tox.ini` and `appveyor.yml`. +# It generates `.travis.yml`, `tox.ini` and `.appveyor.yml`. # # Syntax: [alias:] value [!variable[glob]] [&variable[glob]] # @@ -60,21 +61,21 @@ skip = migrations, south_migrations # - can use as many you want python_versions = - 2.6 - 2.7 - 3.3 - 3.4 - 3.5 + py27 + py35 + py36 + py37 + py38 pypy + pypy3 dependencies = - : trollius !python_versions[3.*] !python_versions[2.6] - : asyncio &python_versions[3.3] - : !python_versions[2.*] !python_versions[3.3] !python_versions[pypy] + : trollius !python_versions[py3*] !python_versions[pypy3] + : !python_versions[py2*] !python_versions[pypy] coverage_flags = - : true - nocover: false + cover: true + nocov: false environment_variables = debug: ASPECTLIB_DEBUG=yes diff --git a/setup.py b/setup.py old mode 100644 new mode 100755 index d48497b..829f0cc --- a/setup.py +++ b/setup.py @@ -16,15 +16,17 @@ def read(*names, **kwargs): - return io.open( + with io.open( join(dirname(__file__), *names), encoding=kwargs.get('encoding', 'utf8') - ).read() + ) as fh: + return fh.read() + setup( name='aspectlib', version='1.4.2', - license='BSD', + license='BSD-2-Clause', description='``aspectlib`` is an aspect-oriented programming, monkey-patch and decorators library. It is useful when changing', long_description='%s\n%s' % ( re.compile('^.. start-badges.*^.. end-badges', re.M | re.S).sub('', read('README.rst')), @@ -47,21 +49,31 @@ def read(*names, **kwargs): 'Operating System :: POSIX', 'Operating System :: Microsoft :: Windows', 'Programming Language :: Python', - 'Programming Language :: Python :: 2.6', 'Programming Language :: Python :: 2.7', 'Programming Language :: Python :: 3', - 'Programming Language :: Python :: 3.3', - 'Programming Language :: Python :: 3.4', 'Programming Language :: Python :: 3.5', + 'Programming Language :: Python :: 3.6', + 'Programming Language :: Python :: 3.7', + 'Programming Language :: Python :: 3.8', 'Programming Language :: Python :: Implementation :: CPython', 'Programming Language :: Python :: Implementation :: PyPy', + # uncomment if you test on these interpreters: + # 'Programming Language :: Python :: Implementation :: IronPython', + # 'Programming Language :: Python :: Implementation :: Jython', + # 'Programming Language :: Python :: Implementation :: Stackless', 'Topic :: Utilities', ], + project_urls={ + 'Documentation': 'https://python-aspectlib.readthedocs.io/', + 'Changelog': 'https://python-aspectlib.readthedocs.io/en/latest/changelog.html', + 'Issue Tracker': 'https://github.com/ionelmc/python-aspectlib/issues', + }, keywords=[ 'aop', 'aspects', 'aspect oriented programming', 'decorators', 'patch', 'monkeypatch', 'weave', 'debug', 'log', 'tests', 'mock', 'capture', 'replay', 'capture-replay', 'debugging', 'patching', 'monkeypatching', 'record', 'recording', 'mocking', 'logger' ], + python_requires='>=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*', install_requires=[ 'fields' ], diff --git a/src/aspectlib/__init__.py b/src/aspectlib/__init__.py index 5ea9f0b..7eb8c87 100644 --- a/src/aspectlib/__init__.py +++ b/src/aspectlib/__init__.py @@ -227,7 +227,7 @@ def advising_generator_wrapper(*args, **kwargs): else: result = None break - except BaseException as exc: + except BaseException as exc: # noqa advice = advisor.throw(*sys.exc_info()) else: try: @@ -325,6 +325,7 @@ def has(self, obj): self._objects[id(obj)] = obj return False + BrokenBag = type('BrokenBag', (), dict(has=lambda self, obj: False))() @@ -337,6 +338,7 @@ def __exit__(self, *_): rollback = __call__ = __exit__ + Nothing = EmptyRollback() diff --git a/src/aspectlib/contrib.py b/src/aspectlib/contrib.py index b4edb5b..727bf0c 100644 --- a/src/aspectlib/contrib.py +++ b/src/aspectlib/contrib.py @@ -65,6 +65,8 @@ def exponential_backoff(count): Wait 2**N seconds. """ return 2 ** count + + retry.exponential_backoff = exponential_backoff @@ -73,6 +75,8 @@ def straight_backoff(count): Wait 1, 2, 5 seconds. All retries after the 3rd retry will wait 5*N-5 seconds. """ return (1, 2, 5)[count] if count < 3 else 5 * count - 5 + + retry.straight_backoff = straight_backoff @@ -81,4 +85,6 @@ def flat_backoff(count): Wait 1, 2, 5, 10, 15, 30 and 60 seconds. All retries after the 5th retry will wait 60 seconds. """ return (1, 2, 5, 10, 15, 30, 60)[count if count < 6 else -1] + + retry.flat_backoff = flat_backoff diff --git a/src/aspectlib/debug.py b/src/aspectlib/debug.py index 16b055e..7e030a1 100644 --- a/src/aspectlib/debug.py +++ b/src/aspectlib/debug.py @@ -34,6 +34,7 @@ def format_stack(skip=0, length=6, _sep=os.path.sep): f.f_code.co_name ) for f in islice(frame_iterator(sys._getframe(1 + skip)), length)) + PRINTABLE = string.digits + string.ascii_letters + string.punctuation + ' ' ASCII_ONLY = ''.join(i if i in PRINTABLE else '.' for i in (chr(c) for c in range(256))) diff --git a/src/aspectlib/pytestsupport.py b/src/aspectlib/pytestsupport.py index cbf5dfc..d8724c5 100644 --- a/src/aspectlib/pytestsupport.py +++ b/src/aspectlib/pytestsupport.py @@ -1,4 +1,5 @@ import pytest + import aspectlib diff --git a/src/aspectlib/test.py b/src/aspectlib/test.py index 3fc72c8..fb7b3e9 100644 --- a/src/aspectlib/test.py +++ b/src/aspectlib/test.py @@ -522,13 +522,14 @@ def replay(self, **options): options.update(self._options) return Replay(self, **options) + ReplayPair = namedtuple("ReplayPair", ('expected', 'actual')) def logged_eval(value, context): try: return eval(value, *context) - except: + except: # noqa logexception("Failed to evaluate %r.\nContext:\n%s", value, ''.join(format_stack( f=_getframe(1), limit=15 diff --git a/src/aspectlib/utils.py b/src/aspectlib/utils.py index ac93440..853cbd2 100644 --- a/src/aspectlib/utils.py +++ b/src/aspectlib/utils.py @@ -19,7 +19,7 @@ if PY3: basestring = str else: - basestring = str, unicode # flake8: noqa + basestring = str, unicode # noqa FIRST_CAP_RE = re.compile('(.)([A-Z][a-z]+)') ALL_CAP_RE = re.compile('([a-z0-9])([A-Z])') @@ -140,6 +140,8 @@ def _make_fixups(): ) except ImportError: continue + + representers.update(_make_fixups()) diff --git a/tox.ini b/tox.ini index 594d288..9e85333 100644 --- a/tox.ini +++ b/tox.ini @@ -2,33 +2,41 @@ envlist = clean, check, - 2.7, - 2.7-debug, - 2.7-nocover, - 2.7-nocover-debug, - 3.3, - 3.3-debug, - 3.3-nocover, - 3.3-nocover-debug, - 3.4, - 3.4-debug, - 3.4-nocover, - 3.4-nocover-debug, - 3.5, - 3.5-debug, - 3.5-nocover, - 3.5-nocover-debug, - pypy, - pypy-debug, - pypy-nocover, - pypy-nocover-debug, - report, - docs + docs, + py27-cover, + py27-cover-debug, + py27-nocov, + py27-nocov-debug, + py35-cover, + py35-cover-debug, + py35-nocov, + py35-nocov-debug, + py36-cover, + py36-cover-debug, + py36-nocov, + py36-nocov-debug, + py37-cover, + py37-cover-debug, + py37-nocov, + py37-nocov-debug, + py38-cover, + py38-cover-debug, + py38-nocov, + py38-nocov-debug, + pypy-cover, + pypy-cover-debug, + pypy-nocov, + pypy-nocov-debug, + pypy3-cover, + pypy3-cover-debug, + pypy3-nocov, + pypy3-nocov-debug, + report [testenv] basepython = - {docs,spell}: python2.7 - {clean,check,report,extension-coveralls,coveralls,codecov}: python3.5 + docs: {env:TOXPYTHON:python3.6} + {bootstrap,clean,check,report,codecov,coveralls}: {env:TOXPYTHON:python3} setenv = PYTHONPATH={toxinidir}/tests PYTHONUNBUFFERED=yes @@ -42,39 +50,18 @@ deps = nose mock tornado - profilestats -commands = - {posargs:py.test -vv --ignore=src} - -[testenv:spell] -setenv = - SPELLCHECK=1 -commands = - sphinx-build -b spelling docs dist/docs -skip_install = true -usedevelop = false -deps = - -r{toxinidir}/docs/requirements.txt - sphinxcontrib-spelling - pyenchant - -[testenv:docs] -deps = - -r{toxinidir}/docs/requirements.txt + pytest-cov + pytest-clarity commands = - sphinx-build {posargs:-E} -b html docs dist/docs - sphinx-build -b linkcheck docs dist/docs + {posargs:pytest --cov --cov-report=term-missing -vv --ignore=src} [testenv:bootstrap] deps = jinja2 matrix skip_install = true -usedevelop = false commands = - python ci/bootstrap.py -passenv = - * + python ci/bootstrap.py --no-env [testenv:check] deps = @@ -85,84 +72,82 @@ deps = pygments isort skip_install = true -usedevelop = false commands = python setup.py check --strict --metadata --restructuredtext check-manifest {toxinidir} - flake8 src tests setup.py - isort --verbose --check-only --diff --recursive src tests setup.py + flake8 + isort --verbose --check-only --diff --recursive + + +[testenv:docs] +usedevelop = true +deps = + -r{toxinidir}/docs/requirements.txt +commands = + sphinx-build {posargs:-E} -b doctest docs dist/docs + sphinx-build {posargs:-E} -b html docs dist/docs + sphinx-build -b linkcheck docs dist/docs [testenv:coveralls] deps = coveralls skip_install = true -usedevelop = false commands = - coverage combine - coverage report coveralls [] + + [testenv:codecov] deps = codecov skip_install = true -usedevelop = false commands = - coverage combine - coverage report - coverage xml --ignore-errors codecov [] - [testenv:report] deps = coverage skip_install = true -usedevelop = false commands = - coverage combine coverage report coverage html [testenv:clean] commands = coverage erase skip_install = true -usedevelop = false deps = coverage -[testenv:2.7] +[testenv:py27-cover] basepython = {env:TOXPYTHON:python2.7} setenv = {[testenv]setenv} - WITH_COVERAGE=yes usedevelop = true commands = - {posargs:py.test --cov --cov-report=term-missing -vv} + {posargs:pytest --cov --cov-report=term-missing -vv} deps = {[testenv]deps} pytest-cov trollius -[testenv:2.7-debug] +[testenv:py27-cover-debug] basepython = {env:TOXPYTHON:python2.7} setenv = {[testenv]setenv} ASPECTLIB_DEBUG=yes - WITH_COVERAGE=yes usedevelop = true commands = - {posargs:py.test --cov --cov-report=term-missing -vv} + {posargs:pytest --cov --cov-report=term-missing -vv} deps = {[testenv]deps} pytest-cov trollius -[testenv:2.7-nocover] +[testenv:py27-nocov] basepython = {env:TOXPYTHON:python2.7} deps = {[testenv]deps} trollius -[testenv:2.7-nocover-debug] +[testenv:py27-nocov-debug] basepython = {env:TOXPYTHON:python2.7} setenv = {[testenv]setenv} @@ -171,150 +156,166 @@ deps = {[testenv]deps} trollius -[testenv:3.3] -basepython = {env:TOXPYTHON:python3.3} +[testenv:py35-cover] +basepython = {env:TOXPYTHON:python3.5} setenv = {[testenv]setenv} - WITH_COVERAGE=yes usedevelop = true commands = - {posargs:py.test --cov --cov-report=term-missing -vv} + {posargs:pytest --cov --cov-report=term-missing -vv} deps = {[testenv]deps} pytest-cov - asyncio -[testenv:3.3-debug] -basepython = {env:TOXPYTHON:python3.3} +[testenv:py35-cover-debug] +basepython = {env:TOXPYTHON:python3.5} setenv = {[testenv]setenv} ASPECTLIB_DEBUG=yes - WITH_COVERAGE=yes usedevelop = true commands = - {posargs:py.test --cov --cov-report=term-missing -vv} + {posargs:pytest --cov --cov-report=term-missing -vv} deps = {[testenv]deps} pytest-cov - asyncio -[testenv:3.3-nocover] -basepython = {env:TOXPYTHON:python3.3} +[testenv:py35-nocov] +basepython = {env:TOXPYTHON:python3.5} + +[testenv:py35-nocov-debug] +basepython = {env:TOXPYTHON:python3.5} +setenv = + {[testenv]setenv} + ASPECTLIB_DEBUG=yes + +[testenv:py36-cover] +basepython = {env:TOXPYTHON:python3.6} +setenv = + {[testenv]setenv} +usedevelop = true +commands = + {posargs:pytest --cov --cov-report=term-missing -vv} deps = {[testenv]deps} - asyncio + pytest-cov -[testenv:3.3-nocover-debug] -basepython = {env:TOXPYTHON:python3.3} +[testenv:py36-cover-debug] +basepython = {env:TOXPYTHON:python3.6} setenv = {[testenv]setenv} ASPECTLIB_DEBUG=yes +usedevelop = true +commands = + {posargs:pytest --cov --cov-report=term-missing -vv} deps = {[testenv]deps} - asyncio + pytest-cov -[testenv:3.4] -basepython = {env:TOXPYTHON:python3.4} +[testenv:py36-nocov] +basepython = {env:TOXPYTHON:python3.6} + +[testenv:py36-nocov-debug] +basepython = {env:TOXPYTHON:python3.6} +setenv = + {[testenv]setenv} + ASPECTLIB_DEBUG=yes + +[testenv:py37-cover] +basepython = {env:TOXPYTHON:python3.7} setenv = {[testenv]setenv} - WITH_COVERAGE=yes usedevelop = true commands = - {posargs:py.test --cov --cov-report=term-missing -vv} + {posargs:pytest --cov --cov-report=term-missing -vv} deps = {[testenv]deps} pytest-cov -[testenv:3.4-debug] -basepython = {env:TOXPYTHON:python3.4} +[testenv:py37-cover-debug] +basepython = {env:TOXPYTHON:python3.7} setenv = {[testenv]setenv} ASPECTLIB_DEBUG=yes - WITH_COVERAGE=yes usedevelop = true commands = - {posargs:py.test --cov --cov-report=term-missing -vv} + {posargs:pytest --cov --cov-report=term-missing -vv} deps = {[testenv]deps} pytest-cov -[testenv:3.4-nocover] -basepython = {env:TOXPYTHON:python3.4} +[testenv:py37-nocov] +basepython = {env:TOXPYTHON:python3.7} -[testenv:3.4-nocover-debug] -basepython = {env:TOXPYTHON:python3.4} +[testenv:py37-nocov-debug] +basepython = {env:TOXPYTHON:python3.7} setenv = {[testenv]setenv} ASPECTLIB_DEBUG=yes -[testenv:3.5] -basepython = {env:TOXPYTHON:python3.5} +[testenv:py38-cover] +basepython = {env:TOXPYTHON:python3.8} setenv = {[testenv]setenv} - WITH_COVERAGE=yes usedevelop = true commands = - {posargs:py.test --cov --cov-report=term-missing -vv} + {posargs:pytest --cov --cov-report=term-missing -vv} deps = {[testenv]deps} pytest-cov -[testenv:3.5-debug] -basepython = {env:TOXPYTHON:python3.5} +[testenv:py38-cover-debug] +basepython = {env:TOXPYTHON:python3.8} setenv = {[testenv]setenv} ASPECTLIB_DEBUG=yes - WITH_COVERAGE=yes usedevelop = true commands = - {posargs:py.test --cov --cov-report=term-missing -vv} + {posargs:pytest --cov --cov-report=term-missing -vv} deps = {[testenv]deps} pytest-cov -[testenv:3.5-nocover] -basepython = {env:TOXPYTHON:python3.5} +[testenv:py38-nocov] +basepython = {env:TOXPYTHON:python3.8} -[testenv:3.5-nocover-debug] -basepython = {env:TOXPYTHON:python3.5} +[testenv:py38-nocov-debug] +basepython = {env:TOXPYTHON:python3.8} setenv = {[testenv]setenv} ASPECTLIB_DEBUG=yes -[testenv:pypy] +[testenv:pypy-cover] basepython = {env:TOXPYTHON:pypy} setenv = {[testenv]setenv} - WITH_COVERAGE=yes usedevelop = true commands = - {posargs:py.test --cov --cov-report=term-missing -vv} + {posargs:pytest --cov --cov-report=term-missing -vv} deps = {[testenv]deps} pytest-cov trollius -[testenv:pypy-debug] +[testenv:pypy-cover-debug] basepython = {env:TOXPYTHON:pypy} setenv = {[testenv]setenv} ASPECTLIB_DEBUG=yes - WITH_COVERAGE=yes usedevelop = true commands = - {posargs:py.test --cov --cov-report=term-missing -vv} + {posargs:pytest --cov --cov-report=term-missing -vv} deps = {[testenv]deps} pytest-cov trollius -[testenv:pypy-nocover] +[testenv:pypy-nocov] basepython = {env:TOXPYTHON:pypy} deps = {[testenv]deps} trollius -[testenv:pypy-nocover-debug] +[testenv:pypy-nocov-debug] basepython = {env:TOXPYTHON:pypy} setenv = {[testenv]setenv} @@ -323,5 +324,34 @@ deps = {[testenv]deps} trollius +[testenv:pypy3-cover] +basepython = {env:TOXPYTHON:pypy3} +setenv = + {[testenv]setenv} +usedevelop = true +commands = + {posargs:pytest --cov --cov-report=term-missing -vv} +deps = + {[testenv]deps} + pytest-cov +[testenv:pypy3-cover-debug] +basepython = {env:TOXPYTHON:pypy3} +setenv = + {[testenv]setenv} + ASPECTLIB_DEBUG=yes +usedevelop = true +commands = + {posargs:pytest --cov --cov-report=term-missing -vv} +deps = + {[testenv]deps} + pytest-cov +[testenv:pypy3-nocov] +basepython = {env:TOXPYTHON:pypy3} + +[testenv:pypy3-nocov-debug] +basepython = {env:TOXPYTHON:pypy3} +setenv = + {[testenv]setenv} + ASPECTLIB_DEBUG=yes From dd963d48671d560bf940388e24d3fc5556a2a26b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ionel=20Cristian=20M=C4=83rie=C8=99?= Date: Tue, 31 Mar 2020 19:42:02 +0300 Subject: [PATCH 147/179] Fix LogCapture to work with "silenced" loggers. --- src/aspectlib/test.py | 21 ++++++++++++++++++--- tests/test_contrib.py | 8 ++++++++ 2 files changed, 26 insertions(+), 3 deletions(-) diff --git a/src/aspectlib/test.py b/src/aspectlib/test.py index fb7b3e9..5146f6c 100644 --- a/src/aspectlib/test.py +++ b/src/aspectlib/test.py @@ -1,9 +1,11 @@ +import logging from collections import defaultdict from collections import namedtuple from difflib import unified_diff from functools import partial from functools import wraps from inspect import isclass +from logging import _checkLevel from logging import getLevelName from logging import getLogger from sys import _getframe @@ -116,15 +118,28 @@ def __enter__(self): self._rollback = weave( self._logger, record(callback=self._callback, extended=True, iscalled=True), - methods='_log$' + methods=('debug', 'info', 'warning', 'error', 'exception', 'critical', 'log') ) return self def __exit__(self, *exc): self._rollback() - def _callback(self, _binding, _qualname, args, _kwargs): - level, message, args = args + def _callback(self, _binding, qualname, args, _kwargs): + _, name = qualname.rsplit('.', 1) + + if name == 'log': + level, args = _checkLevel(args[0]), args[1:] + elif name == 'exception': + level = logging.ERROR + else: + level = _checkLevel(name.upper()) + + if len(args) > 1: + message, args = args[0], args[1:] + else: + message, args = args[0], () + if level >= self._level: self._calls.append(( message % args if args else message, diff --git a/tests/test_contrib.py b/tests/test_contrib.py index d5bd9fd..9eedebe 100644 --- a/tests/test_contrib.py +++ b/tests/test_contrib.py @@ -105,18 +105,26 @@ def __repr__(self): print(i) assert logcap.messages == [ ('ERROR', "__init__((Connection@1, 'to-something'), {}) raised exception Failed. 5 retries left. Sleeping 0 secs."), + ('ERROR', "__init__((Connection@1, 'to-something'), {}) raised exception Failed. 5 retries left. Sleeping 0 secs."), + ('ERROR', "__init__((Connection@2, 'to-something'), {}) raised exception Failed. 4 retries left. Sleeping 0 secs."), ('ERROR', "__init__((Connection@2, 'to-something'), {}) raised exception Failed. 4 retries left. Sleeping 0 secs."), ('INFO', 'connected!'), ('INFO', 'action!'), ('INFO', 'action!'), ('ERROR', 'action((Connection@6, 2, 2), {}) raised exception Failed. 5 retries left. Sleeping 0 secs.'), + ('ERROR', 'action((Connection@6, 2, 2), {}) raised exception Failed. 5 retries left. Sleeping 0 secs.'), + ('ERROR', 'action((Connection@7, 2, 2), {}) raised exception Failed. 4 retries left. Sleeping 0 secs.'), ('ERROR', 'action((Connection@7, 2, 2), {}) raised exception Failed. 4 retries left. Sleeping 0 secs.'), ('ERROR', 'action((Connection@8, 2, 2), {}) raised exception Failed. 3 retries left. Sleeping 0 secs.'), + ('ERROR', 'action((Connection@8, 2, 2), {}) raised exception Failed. 3 retries left. Sleeping 0 secs.'), ('INFO', 'connected!'), ('INFO', 'action!'), ('INFO', 'action!'), ('ERROR', 'action((Connection@12, 4, 4), {}) raised exception Failed. 5 retries left. Sleeping 0 secs.'), + ('ERROR', 'action((Connection@12, 4, 4), {}) raised exception Failed. 5 retries left. Sleeping 0 secs.'), ('ERROR', 'action((Connection@13, 4, 4), {}) raised exception Failed. 4 retries left. Sleeping 0 secs.'), + ('ERROR', 'action((Connection@13, 4, 4), {}) raised exception Failed. 4 retries left. Sleeping 0 secs.'), + ('ERROR', 'action((Connection@14, 4, 4), {}) raised exception Failed. 3 retries left. Sleeping 0 secs.'), ('ERROR', 'action((Connection@14, 4, 4), {}) raised exception Failed. 3 retries left. Sleeping 0 secs.'), ('INFO', 'connected!'), ('INFO', 'action!'), From 857f0851f7e5a43938096b5dcd8cc477cd1f6505 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ionel=20Cristian=20M=C4=83rie=C8=99?= Date: Tue, 31 Mar 2020 20:52:25 +0300 Subject: [PATCH 148/179] Loosen up assertion and switch to linematcher. --- ci/templates/tox.ini | 13 ++--- src/aspectlib/utils.py | 1 + tests/test_aspectlib_test.py | 92 ++++++++++++++---------------------- tox.ini | 13 ++--- 4 files changed, 51 insertions(+), 68 deletions(-) diff --git a/ci/templates/tox.ini b/ci/templates/tox.ini index 8ab015e..338859f 100644 --- a/ci/templates/tox.ini +++ b/ci/templates/tox.ini @@ -18,15 +18,16 @@ setenv = passenv = * deps = + hunter + mock + nose + process-tests pytest - pytest-travis-fold pytest-catchlog - process-tests - nose - mock - tornado - pytest-cov pytest-clarity + pytest-cov + pytest-travis-fold + tornado<6.0 commands = {posargs:pytest --cov --cov-report=term-missing -vv --ignore=src} diff --git a/src/aspectlib/utils.py b/src/aspectlib/utils.py index 853cbd2..4ae24a5 100644 --- a/src/aspectlib/utils.py +++ b/src/aspectlib/utils.py @@ -12,6 +12,7 @@ RegexType = type(re.compile("")) PY3 = sys.version_info[0] == 3 +PY36GT = PY3 and sys.version_info[1] > 6 PY2 = sys.version_info[0] == 2 PY26 = PY2 and sys.version_info[1] == 6 PYPY = platform.python_implementation() == 'PyPy' diff --git a/tests/test_aspectlib_test.py b/tests/test_aspectlib_test.py index 95c893a..05e2c25 100644 --- a/tests/test_aspectlib_test.py +++ b/tests/test_aspectlib_test.py @@ -16,7 +16,11 @@ from aspectlib.utils import PY26 from aspectlib.utils import repr_ex -format_calls = lambda calls: ''.join(_format_calls(calls)) +pytest_plugins = 'pytester', + + +def format_calls(calls): + return ''.join(_format_calls(calls)) def module_fun(a, b=2): @@ -278,6 +282,7 @@ def test_xxx(): # TODO + def test_story_text_helpers(): with Story(test_mod) as story: obj = test_mod.Stuff(1, 2) @@ -297,19 +302,19 @@ def test_story_text_helpers(): test_mod.func(5) test_mod.target(1) - print (replay.missing) + print(replay.missing) assert replay.missing == """stuff_1.meth('b') == 'y' # returns stuff_2 = test_pkg1.test_pkg2.test_mod.Stuff(2, 3) stuff_2.meth('c') == 'z' # returns test_pkg1.test_pkg2.test_mod.target(2) == 3 # returns """ - print (replay.unexpected) + print(replay.unexpected) assert replay.unexpected == """stuff_1.meth() == None # returns stuff_2 = test_pkg1.test_pkg2.test_mod.Stuff(4, 4) stuff_2.meth() == None # returns test_pkg1.test_pkg2.test_mod.func(5) == None # returns """ - print (replay.diff) + print(replay.diff) if PY26: assert replay.diff == """--- expected """ """ +++ actual """ """ @@ -344,7 +349,7 @@ def test_story_text_helpers(): """ -def test_story_empty_play_proxy_class_missing_report(): +def test_story_empty_play_proxy_class_missing_report(LineMatcher): with Story(test_mod).replay(recurse_lock=True, proxy=True, strict=False) as replay: obj = test_mod.Stuff(1, 2) obj.mix(3, 4) @@ -362,55 +367,29 @@ def test_story_empty_play_proxy_class_missing_report(): obj.mix() obj.meth() obj.mix(10) - - print(repr(replay.diff)) - - if PY26: - assert replay.diff == """--- expected """ """ -+++ actual """ """ -@@ -1,0 +1,18 @@ -+stuff_1 = test_pkg1.test_pkg2.test_mod.Stuff(1, 2) -+stuff_1.mix(3, 4) == (1, 2, 3, 4) # returns -+stuff_1.mix('a', 'b') == (1, 2, 'a', 'b') # returns -+stuff_1.raises(123) ** ValueError((123,),) # raises -+stuff_2 = test_pkg1.test_pkg2.test_mod.Stuff(0, 1) -+stuff_2.mix('a', 'b') == (0, 1, 'a', 'b') # returns -+stuff_2.mix(3, 4) == (0, 1, 3, 4) # returns -+test_pkg1.test_pkg2.test_mod.target() == None # returns -+test_pkg1.test_pkg2.test_mod.raises('badarg') ** ValueError(('badarg',),) # raises -+stuff_2.raises(123) ** ValueError((123,),) # raises -+that_long_stuf_1 = test_pkg1.test_pkg2.test_mod.ThatLONGStuf(1) -+that_long_stuf_1.mix(2) == (1, 2) # returns -+that_long_stuf_2 = test_pkg1.test_pkg2.test_mod.ThatLONGStuf(3) -+that_long_stuf_2.mix(4) == (3, 4) # returns -+that_long_stuf_3 = test_pkg1.test_pkg2.test_mod.ThatLONGStuf(2) -+that_long_stuf_3.mix() == (2,) # returns -+that_long_stuf_3.meth() == None # returns -+that_long_stuf_3.mix(10) == (2, 10) # returns -""" - else: - assert replay.diff == """--- expected -+++ actual -@@ -0,0 +1,18 @@ -+stuff_1 = test_pkg1.test_pkg2.test_mod.Stuff(1, 2) -+stuff_1.mix(3, 4) == (1, 2, 3, 4) # returns -+stuff_1.mix('a', 'b') == (1, 2, 'a', 'b') # returns -+stuff_1.raises(123) ** ValueError((123,),) # raises -+stuff_2 = test_pkg1.test_pkg2.test_mod.Stuff(0, 1) -+stuff_2.mix('a', 'b') == (0, 1, 'a', 'b') # returns -+stuff_2.mix(3, 4) == (0, 1, 3, 4) # returns -+test_pkg1.test_pkg2.test_mod.target() == None # returns -+test_pkg1.test_pkg2.test_mod.raises('badarg') ** ValueError(('badarg',),) # raises -+stuff_2.raises(123) ** ValueError((123,),) # raises -+that_long_stuf_1 = test_pkg1.test_pkg2.test_mod.ThatLONGStuf(1) -+that_long_stuf_1.mix(2) == (1, 2) # returns -+that_long_stuf_2 = test_pkg1.test_pkg2.test_mod.ThatLONGStuf(3) -+that_long_stuf_2.mix(4) == (3, 4) # returns -+that_long_stuf_3 = test_pkg1.test_pkg2.test_mod.ThatLONGStuf(2) -+that_long_stuf_3.mix() == (2,) # returns -+that_long_stuf_3.meth() == None # returns -+that_long_stuf_3.mix(10) == (2, 10) # returns -""" + LineMatcher(replay.diff.splitlines()).fnmatch_lines([ + "--- expected", + "+++ actual", + "@@ -0,0 +1,18 @@", + "+stuff_1 = test_pkg1.test_pkg2.test_mod.Stuff(1, 2)", + "+stuff_1.mix(3, 4) == (1, 2, 3, 4) # returns", + "+stuff_1.mix('a', 'b') == (1, 2, 'a', 'b') # returns", + "+stuff_1.raises(123) ** ValueError((123,)*) # raises", + "+stuff_2 = test_pkg1.test_pkg2.test_mod.Stuff(0, 1)", + "+stuff_2.mix('a', 'b') == (0, 1, 'a', 'b') # returns", + "+stuff_2.mix(3, 4) == (0, 1, 3, 4) # returns", + "+test_pkg1.test_pkg2.test_mod.target() == None # returns", + "+test_pkg1.test_pkg2.test_mod.raises('badarg') ** ValueError(('badarg',)*) # raises", + "+stuff_2.raises(123) ** ValueError((123,)*) # raises", + "+that_long_stuf_1 = test_pkg1.test_pkg2.test_mod.ThatLONGStuf(1)", + "+that_long_stuf_1.mix(2) == (1, 2) # returns", + "+that_long_stuf_2 = test_pkg1.test_pkg2.test_mod.ThatLONGStuf(3)", + "+that_long_stuf_2.mix(4) == (3, 4) # returns", + "+that_long_stuf_3 = test_pkg1.test_pkg2.test_mod.ThatLONGStuf(2)", + "+that_long_stuf_3.mix() == (2,) # returns", + "+that_long_stuf_3.meth() == None # returns", + "+that_long_stuf_3.mix(10) == (2, 10) # returns", + ]) def test_story_empty_play_proxy_class(): @@ -556,8 +535,8 @@ def test_story_create(): assert isinstance(obj, test_mod.Stuff) obj.meth('other', 1, 2) == 123 obj.mix('other') == 'mixymix' - #from pprint import pprint as print - #print (dict(story._calls)) + # from pprint import pprint as print + # print (dict(story._calls)) assert dict(story._calls) == { (None, 'test_pkg1.test_pkg2.test_mod.Stuff', "'stuff'", ''): _Binds('stuff_1'), ('stuff_1', 'meth', "'other', 1, 2", ''): _Returns("123"), @@ -567,6 +546,7 @@ def test_story_create(): (None, 'test_pkg1.test_pkg2.test_mod.target', "'a', 'b', 'c'", ''): _Returns("'abc'"), } + def xtest_story_empty_play_proxy_class_dependencies(): with Story(test_mod).replay(recurse_lock=True, proxy=True, strict=False) as replay: obj = test_mod.Stuff(1, 2) diff --git a/tox.ini b/tox.ini index 9e85333..86f64f6 100644 --- a/tox.ini +++ b/tox.ini @@ -43,15 +43,16 @@ setenv = passenv = * deps = + hunter + mock + nose + process-tests pytest - pytest-travis-fold pytest-catchlog - process-tests - nose - mock - tornado - pytest-cov pytest-clarity + pytest-cov + pytest-travis-fold + tornado<6.0 commands = {posargs:pytest --cov --cov-report=term-missing -vv --ignore=src} From a5cfc1d5253e29a226b9a2af789ff46eb467b955 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ionel=20Cristian=20M=C4=83rie=C8=99?= Date: Wed, 1 Apr 2020 11:54:15 +0300 Subject: [PATCH 149/179] Add support for coroutines (async def functions). Ref #11. --- src/aspectlib/__init__.py | 29 +++++- src/aspectlib/py35support.py | 106 ++++++++++++++++++++ src/aspectlib/utils.py | 2 +- tests/test_aspectlib.py | 162 ------------------------------ tests/test_aspectlib_py23.py | 169 ++++++++++++++++++++++++++++++++ tests/test_aspectlib_py3.py | 10 +- tests/test_aspectlib_py37.py | 161 ++++++++++++++++++++++++++++++ tests/test_integrations_py2.py | 4 +- tests/test_integrations_py3.py | 12 ++- tests/test_integrations_py37.py | 23 +++++ 10 files changed, 502 insertions(+), 176 deletions(-) create mode 100644 src/aspectlib/py35support.py create mode 100644 tests/test_aspectlib_py23.py create mode 100644 tests/test_aspectlib_py37.py create mode 100644 tests/test_integrations_py37.py diff --git a/src/aspectlib/__init__.py b/src/aspectlib/__init__.py index 7eb8c87..3ee981b 100644 --- a/src/aspectlib/__init__.py +++ b/src/aspectlib/__init__.py @@ -17,6 +17,7 @@ from .utils import PY2 from .utils import PY3 +from .utils import PY37plus from .utils import Sentinel from .utils import basestring from .utils import force_bind @@ -39,6 +40,21 @@ except ImportError: ClassType = type +try: + from inspect import isasyncgenfunction +except ImportError: + isasyncgenfunction = None + +try: + from inspect import iscoroutinefunction + + def isasyncfunction(obj): + if isasyncgenfunction is None: + return iscoroutinefunction(obj) + else: + return isasyncgenfunction(obj) or iscoroutinefunction(obj) +except ImportError: + isasyncfunction = None __all__ = 'weave', 'Aspect', 'Proceed', 'Return', 'ALL_METHODS', 'NORMAL_METHODS', 'ABSOLUTELY_ALL_METHODS' __version__ = '1.4.2' @@ -159,8 +175,14 @@ def __init__(self, advising_function, bind=False): self.bind = bind def __call__(self, cutpoint_function): - if isgeneratorfunction(cutpoint_function): - if PY3: + if isasyncfunction is not None and isasyncfunction(cutpoint_function): + from aspectlib.py35support import decorate_advising_asyncgenerator_py35 + return decorate_advising_asyncgenerator_py35(self.advising_function, cutpoint_function, self.bind) + elif isgeneratorfunction(cutpoint_function): + if PY37plus: + from aspectlib.py35support import decorate_advising_generator_py35 + return decorate_advising_generator_py35(self.advising_function, cutpoint_function, self.bind) + elif PY3: from aspectlib.py3support import decorate_advising_generator_py3 return decorate_advising_generator_py3(self.advising_function, cutpoint_function, self.bind) else: @@ -244,6 +266,7 @@ def advising_generator_wrapper(*args, **kwargs): raise UnacceptableAdvice("Unknown advice %s" % advice) finally: advisor.close() + return mimic(advising_generator_wrapper, cutpoint_function) else: def advising_function_wrapper(*args, **kwargs): @@ -278,6 +301,7 @@ def advising_function_wrapper(*args, **kwargs): raise UnacceptableAdvice("Unknown advice %s" % advice) finally: advisor.close() + return mimic(advising_function_wrapper, cutpoint_function) @@ -541,6 +565,7 @@ def weave_instance(instance, aspect, methods=NORMAL_METHODS, lazy=False, bag=Bro def fixup(func): return func.__get__(instance, type(instance)) + fixed_aspect = aspect + [fixup] if isinstance(aspect, (list, tuple)) else [aspect, fixup] for attr in dir(instance): diff --git a/src/aspectlib/py35support.py b/src/aspectlib/py35support.py new file mode 100644 index 0000000..493c4b7 --- /dev/null +++ b/src/aspectlib/py35support.py @@ -0,0 +1,106 @@ +from __future__ import print_function + +import sys +from inspect import iscoroutinefunction +from inspect import isgenerator +from inspect import isgeneratorfunction +from logging import getLogger + +from aspectlib import ExpectedGenerator +from aspectlib import Proceed +from aspectlib import Return +from aspectlib import UnacceptableAdvice +from aspectlib import mimic +from aspectlib.utils import logf + +try: + from inspect import isasyncgenfunction +except ImportError: + isasyncgenfunction = iscoroutinefunction + +logger = getLogger(__name__) +logdebug = logf(logger.debug) + + +def decorate_advising_asyncgenerator_py35(advising_function, cutpoint_function, bind): + assert isasyncgenfunction(cutpoint_function) or iscoroutinefunction(cutpoint_function) + + async def advising_asyncgenerator_wrapper_py35(*args, **kwargs): + if bind: + advisor = advising_function(cutpoint_function, *args, **kwargs) + else: + advisor = advising_function(*args, **kwargs) + if not isgenerator(advisor): + raise ExpectedGenerator("advising_function %s did not return a generator." % advising_function) + try: + advice = next(advisor) + while True: + logdebug('Got advice %r from %s', advice, advising_function) + if advice is Proceed or advice is None or isinstance(advice, Proceed): + if isinstance(advice, Proceed): + args = advice.args + kwargs = advice.kwargs + gen = cutpoint_function(*args, **kwargs) + try: + print(' await:', gen) + result = await gen + print('got result from await:', result) + except BaseException: + advice = advisor.throw(*sys.exc_info()) + else: + try: + advice = advisor.send(result) + except StopIteration: + return result + finally: + gen.close() + elif advice is Return: + return + elif isinstance(advice, Return): + return advice.value + else: + raise UnacceptableAdvice("Unknown advice %s" % advice) + finally: + advisor.close() + return mimic(advising_asyncgenerator_wrapper_py35, cutpoint_function) + + +def decorate_advising_generator_py35(advising_function, cutpoint_function, bind): + assert isgeneratorfunction(cutpoint_function) + + def advising_generator_wrapper_py35(*args, **kwargs): + if bind: + advisor = advising_function(cutpoint_function, *args, **kwargs) + else: + advisor = advising_function(*args, **kwargs) + if not isgenerator(advisor): + raise ExpectedGenerator("advising_function %s did not return a generator." % advising_function) + try: + advice = next(advisor) + while True: + logdebug('Got advice %r from %s', advice, advising_function) + if advice is Proceed or advice is None or isinstance(advice, Proceed): + if isinstance(advice, Proceed): + args = advice.args + kwargs = advice.kwargs + gen = cutpoint_function(*args, **kwargs) + try: + result = yield from gen + except BaseException: + advice = advisor.throw(*sys.exc_info()) + else: + try: + advice = advisor.send(result) + except StopIteration: + return result + finally: + gen.close() + elif advice is Return: + return + elif isinstance(advice, Return): + return advice.value + else: + raise UnacceptableAdvice("Unknown advice %s" % advice) + finally: + advisor.close() + return mimic(advising_generator_wrapper_py35, cutpoint_function) diff --git a/src/aspectlib/utils.py b/src/aspectlib/utils.py index 4ae24a5..9e0837e 100644 --- a/src/aspectlib/utils.py +++ b/src/aspectlib/utils.py @@ -12,7 +12,7 @@ RegexType = type(re.compile("")) PY3 = sys.version_info[0] == 3 -PY36GT = PY3 and sys.version_info[1] > 6 +PY37plus = PY3 and sys.version_info[1] >= 7 PY2 = sys.version_info[0] == 2 PY26 = PY2 and sys.version_info[1] == 6 PYPY = platform.python_implementation() == 'PyPy' diff --git a/tests/test_aspectlib.py b/tests/test_aspectlib.py index fa5c802..6146e86 100644 --- a/tests/test_aspectlib.py +++ b/tests/test_aspectlib.py @@ -1304,8 +1304,6 @@ def func(): def test_aspect_on_func_invalid_advice(): - hist = [] - @aspectlib.Aspect def aspect(): yield "stuff" @@ -1376,23 +1374,6 @@ def func(arg=None): assert list(func()) == ['something'] -def test_aspect_on_generator_raise_stopiteration(): - result = [] - - @aspectlib.Aspect - def aspect(): - val = yield aspectlib.Proceed - result.append(val) - - @aspect - def func(): - raise StopIteration('something') - yield - - assert list(func()) == [] - assert result == ['something'] - - def test_aspect_on_generator_close(): excs = [] @@ -1487,86 +1468,6 @@ def func(): assert values == [1, 3] -def test_aspect_on_generator_result_from_aspect(): - @aspectlib.Aspect - def aspect(): - yield aspectlib.Proceed - yield aspectlib.Return('result') - - @aspect - def func(): - yield 'something' - - gen = func() - try: - while 1: - next(gen) - except StopIteration as exc: - assert exc.args == ('result',) - else: - raise AssertionError("did not raise StopIteration") - - -def test_aspect_on_generator_result(): - result = [] - - @aspectlib.Aspect - def aspect(): - result.append((yield aspectlib.Proceed)) - - @aspect - def func(): - yield 'something' - raise StopIteration('value') - - assert list(func()) == ['something'] - assert result == ['value'] - - -def test_aspect_on_coroutine(): - hist = [] - - @aspectlib.Aspect - def aspect(): - try: - hist.append('before') - hist.append((yield aspectlib.Proceed)) - hist.append('after') - except Exception: - hist.append('error') - finally: - hist.append('finally') - try: - hist.append((yield aspectlib.Return)) - except GeneratorExit: - hist.append('closed') - raise - else: - hist.append('consumed') - hist.append('bad-suffix') - - @aspect - def func(): - val = 99 - for _ in range(3): - print("YIELD", val + 1) - val = yield val + 1 - print("GOT", val) - raise StopIteration("the-return-value") - - gen = func() - data = [] - try: - for i in [None, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9]: - data.append(gen.send(i)) - except StopIteration: - data.append('done') - print(data) - assert data == [100, 1, 2, 'done'], hist - print(hist) - assert hist == ['before', 'the-return-value', 'after', 'finally', 'closed'] - - def test_weave_module(strmod=None): calls = [] from test_pkg1.test_pkg2 import test_mod @@ -1584,69 +1485,6 @@ def test_weave_module_as_str(): test_weave_module("test_pkg1.test_pkg2.test_mod") -def test_aspect_chain_on_generator(): - @aspectlib.Aspect - def foo(arg): - result = yield aspectlib.Proceed(arg + 1) - yield aspectlib.Return(result - 1) - - @foo - @foo - @foo - def func(a): - assert a == 3 - raise StopIteration(a) - yield - - gen = func(0) - result = pytest.raises(StopIteration, gen.__next__ if hasattr(gen, '__next__') else gen.next) - assert result.value.args == (0,) - - -def test_aspect_chain_on_generator_no_return(): - @aspectlib.Aspect - def foo(arg): - result = yield aspectlib.Proceed(arg + 1) - yield aspectlib.Return(result) - - @foo - @foo - @foo - def func(a): - assert a == 3 - yield - - gen = func(0) - if hasattr(gen, '__next__'): - assert gen.__next__() is None - result = pytest.raises(StopIteration, gen.__next__) - else: - assert gen.next() is None - result = pytest.raises(StopIteration, gen.next) - assert result.value.args == (None,) - - -def test_aspect_chain_on_generator_no_return_advice(): - @aspectlib.Aspect - def foo(arg): - yield aspectlib.Proceed(arg + 1) - - @foo - @foo - @foo - def func(a): - assert a == 3 - raise StopIteration(a) - yield - - gen = func(0) - if hasattr(gen, '__next__'): - result = pytest.raises(StopIteration, gen.__next__) - else: - result = pytest.raises(StopIteration, gen.next) - assert result.value.args == (3,) - - def test_weave_method(): calls = [] intercepted = [] diff --git a/tests/test_aspectlib_py23.py b/tests/test_aspectlib_py23.py new file mode 100644 index 0000000..6a65209 --- /dev/null +++ b/tests/test_aspectlib_py23.py @@ -0,0 +1,169 @@ +# encoding: utf8 +from __future__ import print_function + +import pytest + +import aspectlib +from aspectlib.utils import PY37plus + +pytestmark = pytest.mark.skipif(PY37plus, reason="Tests are incompatible with PEP-479") + + +def test_aspect_on_generator_result(): + result = [] + + @aspectlib.Aspect + def aspect(): + result.append((yield aspectlib.Proceed)) + + @aspect + def func(): + yield 'something' + raise StopIteration('value') + + assert list(func()) == ['something'] + assert result == ['value'] + + +def test_aspect_on_coroutine(): + hist = [] + + @aspectlib.Aspect + def aspect(): + try: + hist.append('before') + hist.append((yield aspectlib.Proceed)) + hist.append('after') + except Exception: + hist.append('error') + finally: + hist.append('finally') + try: + hist.append((yield aspectlib.Return)) + except GeneratorExit: + hist.append('closed') + raise + else: + hist.append('consumed') + hist.append('bad-suffix') + + @aspect + def func(): + val = 99 + for _ in range(3): + print("YIELD", val + 1) + val = yield val + 1 + print("GOT", val) + raise StopIteration("the-return-value") + + gen = func() + data = [] + try: + for i in [None, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9]: + data.append(gen.send(i)) + except StopIteration: + data.append('done') + print(data) + assert data == [100, 1, 2, 'done'], hist + print(hist) + assert hist == ['before', 'the-return-value', 'after', 'finally', 'closed'] + + +def test_aspect_chain_on_generator(): + @aspectlib.Aspect + def foo(arg): + result = yield aspectlib.Proceed(arg + 1) + yield aspectlib.Return(result - 1) + + @foo + @foo + @foo + def func(a): + assert a == 3 + raise StopIteration(a) + yield + + gen = func(0) + result = pytest.raises(StopIteration, gen.__next__ if hasattr(gen, '__next__') else gen.next) + assert result.value.args == (0,) + + +def test_aspect_chain_on_generator_no_return_advice(): + @aspectlib.Aspect + def foo(arg): + yield aspectlib.Proceed(arg + 1) + + @foo + @foo + @foo + def func(a): + assert a == 3 + raise StopIteration(a) + yield + + gen = func(0) + if hasattr(gen, '__next__'): + result = pytest.raises(StopIteration, gen.__next__) + else: + result = pytest.raises(StopIteration, gen.next) + assert result.value.args == (3,) + + +def test_aspect_on_generator_raise_stopiteration(): + result = [] + + @aspectlib.Aspect + def aspect(): + val = yield aspectlib.Proceed + result.append(val) + + @aspect + def func(): + raise StopIteration('something') + yield + + assert list(func()) == [] + assert result == ['something'] + + +def test_aspect_on_generator_result_from_aspect(): + @aspectlib.Aspect + def aspect(): + yield aspectlib.Proceed + yield aspectlib.Return('result') + + @aspect + def func(): + yield 'something' + + gen = func() + try: + while 1: + next(gen) + except StopIteration as exc: + assert exc.args == ('result',) + else: + raise AssertionError("did not raise StopIteration") + + +def test_aspect_chain_on_generator_no_return(): + @aspectlib.Aspect + def foo(arg): + result = yield aspectlib.Proceed(arg + 1) + yield aspectlib.Return(result) + + @foo + @foo + @foo + def func(a): + assert a == 3 + yield + + gen = func(0) + if hasattr(gen, '__next__'): + assert gen.__next__() is None + result = pytest.raises(StopIteration, gen.__next__) + else: + assert gen.next() is None + result = pytest.raises(StopIteration, gen.next) + assert result.value.args == (None,) diff --git a/tests/test_aspectlib_py3.py b/tests/test_aspectlib_py3.py index 63d4e1d..db9864d 100644 --- a/tests/test_aspectlib_py3.py +++ b/tests/test_aspectlib_py3.py @@ -38,10 +38,8 @@ def func(a): assert a == 3 yield - gen = func(0) - assert gen.__next__() is None - result = pytest.raises(StopIteration, gen.__next__) - assert result.value.args == (None,) + result = consume(func(0)) + assert result is None def consume(gen): @@ -69,7 +67,7 @@ def func(a): yield gen = func(0) - assert consume(func(0)) == 0 + assert consume(gen) == 0 def test_aspect_chain_on_generator_no_return_yield_from(): @@ -86,4 +84,4 @@ def func(a): yield gen = func(0) - assert consume(func(0)) is None + assert consume(gen) is None diff --git a/tests/test_aspectlib_py37.py b/tests/test_aspectlib_py37.py new file mode 100644 index 0000000..43e340b --- /dev/null +++ b/tests/test_aspectlib_py37.py @@ -0,0 +1,161 @@ +# encoding: utf8 +from __future__ import print_function + +import pytest + +import aspectlib +from aspectlib.utils import PY37plus + +from test_aspectlib_py3 import consume + +pytestmark = pytest.mark.skipif(not PY37plus, reason="Tests only work with PEP-479") + + +def test_aspect_on_generator_result(): + result = [] + + @aspectlib.Aspect + def aspect(): + result.append((yield aspectlib.Proceed)) + + @aspect + def func(): + yield 'something' + return 'value' + + assert list(func()) == ['something'] + assert result == ['value'] + + +def test_aspect_on_coroutine(): + hist = [] + + @aspectlib.Aspect + def aspect(): + try: + hist.append('before') + hist.append((yield aspectlib.Proceed)) + hist.append('after') + except Exception: + hist.append('error') + finally: + hist.append('finally') + try: + hist.append((yield aspectlib.Return)) + except GeneratorExit: + hist.append('closed') + raise + else: + hist.append('consumed') + hist.append('bad-suffix') + + @aspect + def func(): + val = 99 + for _ in range(3): + print("YIELD", val + 1) + val = yield val + 1 + print("GOT", val) + return "the-return-value" + + gen = func() + data = [] + try: + for i in [None, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9]: + data.append(gen.send(i)) + except StopIteration: + data.append('done') + print(data) + assert data == [100, 1, 2, 'done'], hist + print(hist) + assert hist == ['before', 'the-return-value', 'after', 'finally', 'closed'] + + +def test_aspect_chain_on_generator(): + @aspectlib.Aspect + def foo(arg): + result = yield aspectlib.Proceed(arg + 1) + yield aspectlib.Return(result - 1) + + @foo + @foo + @foo + def func(a): + assert a == 3 + return a + yield + + gen = func(0) + result = pytest.raises(StopIteration, gen.__next__ if hasattr(gen, '__next__') else gen.next) + assert result.value.args == (0,) + + +def test_aspect_chain_on_generator_no_return_advice(): + @aspectlib.Aspect + def foo(arg): + yield aspectlib.Proceed(arg + 1) + + @foo + @foo + @foo + def func(a): + assert a == 3 + return a + yield + + gen = func(0) + assert consume(gen) == 3 + + +def test_aspect_on_generator_raise_stopiteration(): + result = [] + + @aspectlib.Aspect + def aspect(): + val = yield aspectlib.Proceed + result.append(val) + + @aspect + def func(): + return 'something' + yield + + assert list(func()) == [] + assert result == ['something'] + + +def test_aspect_on_generator_result_from_aspect(): + @aspectlib.Aspect + def aspect(): + yield aspectlib.Proceed + yield aspectlib.Return('result') + + @aspect + def func(): + yield 'something' + + gen = func() + try: + while 1: + next(gen) + except StopIteration as exc: + assert exc.args == ('result',) + else: + raise AssertionError("did not raise StopIteration") + + +def test_aspect_chain_on_generator_no_return(): + @aspectlib.Aspect + def foo(arg): + result = yield aspectlib.Proceed(arg + 1) + yield aspectlib.Return(result) + + @foo + @foo + @foo + def func(a): + assert a == 3 + yield + + gen = func(0) + assert consume(gen) is None diff --git a/tests/test_integrations_py2.py b/tests/test_integrations_py2.py index ceeda45..fc05188 100644 --- a/tests/test_integrations_py2.py +++ b/tests/test_integrations_py2.py @@ -5,13 +5,14 @@ from datetime import timedelta import pytest -import trollius as asyncio from tornado import gen from tornado import ioloop from aspectlib import debug from aspectlib.test import Story +asyncio = pytest.importorskip("trollius") + try: import MySQLdb except ImportError: @@ -47,6 +48,7 @@ def coro(): output = buf.getvalue() assert 'coro => %r' % 'result' in output + @pytest.mark.skipif(not MySQLdb, reason="No MySQLdb installed") def test_mysql(): with Story(['MySQLdb.cursors.BaseCursor', 'MySQLdb.connections.Connection']) as story: diff --git a/tests/test_integrations_py3.py b/tests/test_integrations_py3.py index 898c0ed..e025eac 100644 --- a/tests/test_integrations_py3.py +++ b/tests/test_integrations_py3.py @@ -1,16 +1,20 @@ -try: - from StringIO import StringIO -except ImportError: - from io import StringIO import asyncio from datetime import timedelta +import pytest from tornado import gen from tornado import ioloop +from aspectlib import PY37plus from aspectlib import debug +try: + from StringIO import StringIO +except ImportError: + from io import StringIO + +@pytest.mark.skipif(PY37plus, reason="Test is incompatible with PEP-479") def test_decorate_asyncio_coroutine(): buf = StringIO() diff --git a/tests/test_integrations_py37.py b/tests/test_integrations_py37.py new file mode 100644 index 0000000..2e77762 --- /dev/null +++ b/tests/test_integrations_py37.py @@ -0,0 +1,23 @@ +import asyncio + +from aspectlib import debug + +try: + from StringIO import StringIO +except ImportError: + from io import StringIO + + +def test_decorate_asyncio_coroutine(): + buf = StringIO() + + @debug.log(print_to=buf, module=False, stacktrace=2, result_repr=repr) + async def coro(): + await asyncio.sleep(0.01) + return "result" + + loop = asyncio.get_event_loop() + loop.run_until_complete(coro()) + output = buf.getvalue() + print(output) + assert 'coro => %r' % 'result' in output From c2f59585ec78086e1aca2a5a9d26919887b934d4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ionel=20Cristian=20M=C4=83rie=C8=99?= Date: Sun, 5 Apr 2020 10:57:29 +0300 Subject: [PATCH 150/179] Some styliing. --- .cookiecutterrc | 108 ++++++++++++++++++++++++------------------------ README.rst | 2 +- setup.cfg | 4 +- 3 files changed, 57 insertions(+), 57 deletions(-) diff --git a/.cookiecutterrc b/.cookiecutterrc index 14607e8..113ace3 100644 --- a/.cookiecutterrc +++ b/.cookiecutterrc @@ -1,56 +1,56 @@ # Generated by cookiepatcher, a small shim around cookiecutter (pip install cookiepatcher) -!!omap -- cookiecutter: !!omap - - full_name: Ionel Cristian Mărieș - - email: contact@ionelmc.ro - - website: http://blog.ionelmc.ro - - project_name: Aspectlib - - repo_name: python-aspectlib - - repo_hosting: github.com - - repo_hosting_domain: github.com - - repo_username: ionelmc - - package_name: aspectlib - - distribution_name: aspectlib - - project_short_description: '``aspectlib`` is an aspect-oriented programming, monkey-patch and decorators library. It is useful when changing' - - release_date: '2016-05-10' - - year_from: '2014' - - year_to: '2020' - - version: 1.4.2 - - license: BSD 2-Clause License - - c_extension_support: no - - c_extension_optional: no - - c_extension_module: '-' - - c_extension_function: '-' - - c_extension_test_pypi: no - - c_extension_test_pypi_username: ionelmc - - test_matrix_configurator: yes - - test_matrix_separate_coverage: no - - test_runner: pytest - - setup_py_uses_test_runner: no - - setup_py_uses_setuptools_scm: no - - pypi_badge: yes - - pypi_disable_upload: no - - allow_tests_inside_package: no - - linter: flake8 - - command_line_interface: no - - command_line_interface_bin_name: '-' - - coveralls: yes - - coveralls_token: mHSWktPkrw65WSlziG8NnK9m1xj2k1kOQ - - codecov: yes - - landscape: no - - scrutinizer: no - - codacy: no - - codacy_projectid: '-' - - codeclimate: no - - sphinx_docs: yes - - sphinx_theme: sphinx-py3doc-enhanced-theme - - sphinx_doctest: yes - - sphinx_docs_hosting: https://python-aspectlib.readthedocs.io/ - - travis: yes - - travis_osx: no - - appveyor: yes - - requiresio: yes - - _extensions: - - jinja2_time.TimeExtension - - _template: /home/ionel/open-source/cookiecutter-pylibrary +cookiecutter: + _extensions: + - jinja2_time.TimeExtension + _template: /home/ionel/open-source/cookiecutter-pylibrary + allow_tests_inside_package: no + appveyor: yes + c_extension_function: '-' + c_extension_module: '-' + c_extension_optional: no + c_extension_support: no + c_extension_test_pypi: no + c_extension_test_pypi_username: ionelmc + codacy: no + codacy_projectid: '-' + codeclimate: no + codecov: yes + command_line_interface: no + command_line_interface_bin_name: '-' + coveralls: yes + coveralls_token: mHSWktPkrw65WSlziG8NnK9m1xj2k1kOQ + distribution_name: aspectlib + email: contact@ionelmc.ro + full_name: Ionel Cristian Mărieș + landscape: no + license: BSD 2-Clause License + linter: flake8 + package_name: aspectlib + pre_commit: yes + project_name: Aspectlib + project_short_description: '``aspectlib`` is an aspect-oriented programming, monkey-patch and decorators library. It is useful when changing' + pypi_badge: yes + pypi_disable_upload: no + release_date: '2016-05-10' + repo_hosting: github.com + repo_hosting_domain: github.com + repo_name: python-aspectlib + repo_username: ionelmc + requiresio: yes + scrutinizer: no + setup_py_uses_setuptools_scm: no + setup_py_uses_test_runner: no + sphinx_docs: yes + sphinx_docs_hosting: https://python-aspectlib.readthedocs.io/ + sphinx_doctest: yes + sphinx_theme: sphinx-py3doc-enhanced-theme + test_matrix_configurator: yes + test_matrix_separate_coverage: no + test_runner: pytest + travis: yes + travis_osx: no + version: 1.4.2 + website: http://blog.ionelmc.ro + year_from: '2014' + year_to: '2020' diff --git a/README.rst b/README.rst index f9b6e95..cbffd9c 100644 --- a/README.rst +++ b/README.rst @@ -67,7 +67,7 @@ Overview behavior in existing code is desired. It includes tools for debugging and testing: simple mock/record and a complete capture/replay framework. -* Free software: BSD license +* Free software: BSD 2-Clause License Installation ============ diff --git a/setup.cfg b/setup.cfg index c2e0b21..14d07c4 100644 --- a/setup.cfg +++ b/setup.cfg @@ -3,7 +3,7 @@ universal = 1 [flake8] max-line-length = 140 -exclude = .tox ci/templates +exclude = .tox,ci/templates [tool:pytest] # If a pytest section is found in one of the possible config files @@ -37,7 +37,7 @@ known_first_party = aspectlib default_section = THIRDPARTY forced_separate = test_aspectlib not_skip = __init__.py -skip = .tox ci/templates +skip = .tox,ci/templates [matrix] # This is the configuration for the `./bootstrap.py` script. From 81aa94dabc25fbebcc301d4d32890075e3a40d7a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ionel=20Cristian=20M=C4=83rie=C8=99?= Date: Sun, 5 Apr 2020 11:07:36 +0300 Subject: [PATCH 151/179] Fix some styling. --- tests/test_aspectlib_debug.py | 22 ++++++++++++++-------- tests/test_integrations.py | 8 ++------ tests/test_pkg1/test_pkg2/test_mod.py | 1 + 3 files changed, 17 insertions(+), 14 deletions(-) diff --git a/tests/test_aspectlib_debug.py b/tests/test_aspectlib_debug.py index 631e6bb..94eff55 100644 --- a/tests/test_aspectlib_debug.py +++ b/tests/test_aspectlib_debug.py @@ -15,9 +15,9 @@ except ImportError: from io import StringIO - LOG_TEST_SIMPLE = r'''^some_meth\(1, 2, 3, a=4\) +<<< .*tests/test_aspectlib_debug.py:\d+:test_simple.* -some_meth => \.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\. !"#\$%&\'\(\)\*\+,-\./0123456789:;<=>\?@ABCDEFGHIJKLMNOPQRSTUVWXYZ\[\\\]\^_`abcdefghijklmnopqrstuvwxyz\{\|\}~\.+ +some_meth => \.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\. !"#\$%&\'\(\)\*\+,-\./0123456789:;<=>\?@''' \ +r'''ABCDEFGHIJKLMNOPQRSTUVWXYZ\[\\\]\^_`abcdefghijklmnopqrstuvwxyz\{\|\}~\.+ $''' @@ -86,9 +86,11 @@ def test_attributes(): ), methods='(?!bar)(?!__.*__$)'): MyStuff('bar').stuff() print(buf.getvalue()) - assert re.match(r"^\{test_aspectlib_debug.MyStuff foo='bar' bar='foo'\}.stuff\(\) +<<< .*tests/test_aspectlib_debug.py:\d+:test_attributes.*\n\{test_aspectlib_debug.MyStuff foo='bar' bar='foo'\}.stuff => bar\n$", buf.getvalue()) + assert re.match(r"^\{test_aspectlib_debug.MyStuff foo='bar' bar='foo'\}.stuff\(\) +<<< .*tests/test_aspectlib_debug.py:\d+:" + r"test_attributes.*\n\{test_aspectlib_debug.MyStuff foo='bar' bar='foo'\}.stuff => bar\n$", buf.getvalue()) MyStuff('bar').stuff() - assert re.match(r"^\{test_aspectlib_debug.MyStuff foo='bar' bar='foo'\}.stuff\(\) +<<< .*tests/test_aspectlib_debug.py:\d+:test_attributes.*\n\{test_aspectlib_debug.MyStuff foo='bar' bar='foo'\}.stuff => bar\n$", buf.getvalue()) + assert re.match(r"^\{test_aspectlib_debug.MyStuff foo='bar' bar='foo'\}.stuff\(\) +<<< .*tests/test_aspectlib_debug.py:\d+:" + r"test_attributes.*\n\{test_aspectlib_debug.MyStuff foo='bar' bar='foo'\}.stuff => bar\n$", buf.getvalue()) def test_no_stack(): @@ -100,7 +102,8 @@ def test_no_stack(): ), methods='(?!bar)(?!__.*__$)'): MyStuff('bar').stuff() print(buf.getvalue()) - assert "{test_aspectlib_debug.MyStuff foo='bar' bar='foo'}.stuff()\n{test_aspectlib_debug.MyStuff foo='bar' bar='foo'}.stuff => bar\n" == buf.getvalue() + assert "{test_aspectlib_debug.MyStuff foo='bar' bar='foo'}.stuff()\n" \ + "{test_aspectlib_debug.MyStuff foo='bar' bar='foo'}.stuff => bar\n" == buf.getvalue() def test_attributes_old_style(): @@ -112,9 +115,11 @@ def test_attributes_old_style(): ), methods='(?!bar)(?!__.*__$)'): OldStuff('bar').stuff() print(repr(buf.getvalue())) - assert re.match(r"^\{test_aspectlib_debug.OldStuff foo='bar' bar='foo'\}.stuff\(\) +<<< .*tests/test_aspectlib_debug.py:\d+:test_attributes.*\n\{test_aspectlib_debug.OldStuff foo='bar' bar='foo'\}.stuff => bar\n$", buf.getvalue()) + assert re.match(r"^\{test_aspectlib_debug.OldStuff foo='bar' bar='foo'\}.stuff\(\) +<<< .*tests/test_aspectlib_debug.py:\d+:" + r"test_attributes.*\n\{test_aspectlib_debug.OldStuff foo='bar' bar='foo'\}.stuff => bar\n$", buf.getvalue()) MyStuff('bar').stuff() - assert re.match(r"^\{test_aspectlib_debug.OldStuff foo='bar' bar='foo'\}.stuff\(\) +<<< .*tests/test_aspectlib_debug.py:\d+:test_attributes.*\n\{test_aspectlib_debug.OldStuff foo='bar' bar='foo'\}.stuff => bar\n$", buf.getvalue()) + assert re.match(r"^\{test_aspectlib_debug.OldStuff foo='bar' bar='foo'\}.stuff\(\) +<<< .*tests/test_aspectlib_debug.py:\d+:" + r"test_attributes.*\n\{test_aspectlib_debug.OldStuff foo='bar' bar='foo'\}.stuff => bar\n$", buf.getvalue()) def test_no_stack_old_style(): @@ -126,7 +131,8 @@ def test_no_stack_old_style(): ), methods='(?!bar)(?!__.*__$)'): OldStuff('bar').stuff() print(buf.getvalue()) - assert "{test_aspectlib_debug.OldStuff foo='bar' bar='foo'}.stuff()\n{test_aspectlib_debug.OldStuff foo='bar' bar='foo'}.stuff => bar\n" == buf.getvalue() + assert "{test_aspectlib_debug.OldStuff foo='bar' bar='foo'}.stuff()\n" \ + "{test_aspectlib_debug.OldStuff foo='bar' bar='foo'}.stuff => bar\n" == buf.getvalue() @pytest.mark.skipif(sys.version_info < (2, 7), reason="No weakref.WeakSet on Python<=2.6") diff --git a/tests/test_integrations.py b/tests/test_integrations.py index 5ae32cb..e4dc004 100644 --- a/tests/test_integrations.py +++ b/tests/test_integrations.py @@ -14,11 +14,6 @@ from aspectlib.test import record from aspectlib.utils import PYPY -try: - import thread -except ImportError: - import _thread as thread - try: from StringIO import StringIO except ImportError: @@ -62,6 +57,7 @@ def test_fork(): os._exit(0) assert pid != 'foobar' + def test_socket(target=socket.socket): buf = StringIO() with aspectlib.weave(target, aspectlib.debug.log( @@ -116,7 +112,7 @@ def test_socket_all_methods(): lazy=True, methods=aspectlib.ALL_METHODS ): - s = socket.socket() + socket.socket() assert "}.__init__ => None" in buf.getvalue() diff --git a/tests/test_pkg1/test_pkg2/test_mod.py b/tests/test_pkg1/test_pkg2/test_mod.py index 4db7100..729259a 100644 --- a/tests/test_pkg1/test_pkg2/test_mod.py +++ b/tests/test_pkg1/test_pkg2/test_mod.py @@ -9,6 +9,7 @@ def func(*a): def raises(*a): raise ValueError(a) + a = 1 From 8e658b4b89724e1988c12c48f6bd3025e269aef4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ionel=20Cristian=20M=C4=83rie=C8=99?= Date: Sun, 5 Apr 2020 11:35:52 +0300 Subject: [PATCH 152/179] Exclude bumpversion conf. --- .pre-commit-config.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index 49285a7..e0946b3 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -2,7 +2,7 @@ # pre-commit install # To update the pre-commit hooks run: # pre-commit install-hooks -exclude: '^(.tox|ci/templates)/' +exclude: '^(.tox|ci/templates|.bumpversion.cfg)(/|$)' repos: - repo: https://github.com/pre-commit/pre-commit-hooks rev: master From 6d8a33eb7f26392a3fd55e127892663a79e28400 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ionel=20Cristian=20M=C4=83rie=C8=99?= Date: Sun, 5 Apr 2020 11:37:01 +0300 Subject: [PATCH 153/179] Remove broken link. --- docs/examples.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/examples.rst b/docs/examples.rst index 038fa63..0522843 100644 --- a/docs/examples.rst +++ b/docs/examples.rst @@ -98,7 +98,7 @@ Profiling There's no decorator for such in aspectlib but you can use any of the many choices on `PyPI `_. -Here's one example with `profilestats `_: +Here's one example with `profilestats`: .. code-block:: pycon From 60f35b9ec5a8047f180c8f647cf4c5f5d7004922 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ionel=20Cristian=20M=C4=83rie=C8=99?= Date: Sun, 5 Apr 2020 11:40:03 +0300 Subject: [PATCH 154/179] Update changelog. --- CHANGELOG.rst | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/CHANGELOG.rst b/CHANGELOG.rst index 0d8eb69..1388d9a 100644 --- a/CHANGELOG.rst +++ b/CHANGELOG.rst @@ -1,10 +1,12 @@ Changelog ========= -unreleased (2019-07-19) ------------------------ +1.5.0 (2020-04-05) +------------------ -* During weaving, stop reading attributes don't match the method selector. +* Fixed ``weave`` to stop reading attributes that don't match the method selector. + Contributed by Jonas Maurus in `#14 `_. +* Added support for Python 3.7 and 3.8 (``async def`` functions and generators). 1.4.2 (2016-05-10) ------------------ @@ -21,7 +23,7 @@ unreleased (2019-07-19) * Corrected weaving of methods, the weaved function should be unbound. * Rolling back only applies undos once. -* Added a convenience `weave` fixture for pytest. +* Added a convenience ``weave`` fixture for pytest. 1.3.3 (2015-10-02) ------------------ From 309c89fedbe418af0e5a3de6cfcde1370771ac9f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ionel=20Cristian=20M=C4=83rie=C8=99?= Date: Sun, 5 Apr 2020 12:03:47 +0300 Subject: [PATCH 155/179] =?UTF-8?q?Bump=20version:=201.4.2=20=E2=86=92=201?= =?UTF-8?q?.5.0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .bumpversion.cfg | 3 ++- README.rst | 4 ++-- docs/conf.py | 2 +- setup.py | 2 +- src/aspectlib/__init__.py | 2 +- 5 files changed, 7 insertions(+), 6 deletions(-) diff --git a/.bumpversion.cfg b/.bumpversion.cfg index c0b2e4c..0660c61 100644 --- a/.bumpversion.cfg +++ b/.bumpversion.cfg @@ -1,5 +1,5 @@ [bumpversion] -current_version = 1.4.2 +current_version = 1.5.0 commit = True tag = True @@ -18,3 +18,4 @@ replace = version = release = '{new_version}' [bumpversion:file:src/aspectlib/__init__.py] search = __version__ = '{current_version}' replace = __version__ = '{new_version}' + diff --git a/README.rst b/README.rst index cbffd9c..c397be2 100644 --- a/README.rst +++ b/README.rst @@ -55,9 +55,9 @@ Overview :alt: Supported implementations :target: https://pypi.org/project/aspectlib -.. |commits-since| image:: https://img.shields.io/github/commits-since/ionelmc/python-aspectlib/v1.4.2.svg +.. |commits-since| image:: https://img.shields.io/github/commits-since/ionelmc/python-aspectlib/v1.5.0.svg :alt: Commits since latest release - :target: https://github.com/ionelmc/python-aspectlib/compare/v1.4.2...master + :target: https://github.com/ionelmc/python-aspectlib/compare/v1.5.0...master diff --git a/docs/conf.py b/docs/conf.py index 929671b..4b7690c 100644 --- a/docs/conf.py +++ b/docs/conf.py @@ -20,7 +20,7 @@ year = '2014-2020' author = 'Ionel Cristian Mărieș' copyright = '{0}, {1}'.format(year, author) -version = release = '1.4.2' +version = release = '1.5.0' pygments_style = 'trac' templates_path = ['.'] diff --git a/setup.py b/setup.py index 829f0cc..6846710 100755 --- a/setup.py +++ b/setup.py @@ -25,7 +25,7 @@ def read(*names, **kwargs): setup( name='aspectlib', - version='1.4.2', + version='1.5.0', license='BSD-2-Clause', description='``aspectlib`` is an aspect-oriented programming, monkey-patch and decorators library. It is useful when changing', long_description='%s\n%s' % ( diff --git a/src/aspectlib/__init__.py b/src/aspectlib/__init__.py index 3ee981b..d5f1bff 100644 --- a/src/aspectlib/__init__.py +++ b/src/aspectlib/__init__.py @@ -57,7 +57,7 @@ def isasyncfunction(obj): isasyncfunction = None __all__ = 'weave', 'Aspect', 'Proceed', 'Return', 'ALL_METHODS', 'NORMAL_METHODS', 'ABSOLUTELY_ALL_METHODS' -__version__ = '1.4.2' +__version__ = '1.5.0' logger = getLogger(__name__) logdebug = logf(logger.debug) From 1866571ab9d8c283b276dd885939d3a403b0fdfb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ionel=20Cristian=20M=C4=83rie=C8=99?= Date: Thu, 11 Jun 2020 17:29:20 +0300 Subject: [PATCH 156/179] Remove debug prints. --- src/aspectlib/py35support.py | 2 -- 1 file changed, 2 deletions(-) diff --git a/src/aspectlib/py35support.py b/src/aspectlib/py35support.py index 493c4b7..27e2c9a 100644 --- a/src/aspectlib/py35support.py +++ b/src/aspectlib/py35support.py @@ -42,9 +42,7 @@ async def advising_asyncgenerator_wrapper_py35(*args, **kwargs): kwargs = advice.kwargs gen = cutpoint_function(*args, **kwargs) try: - print(' await:', gen) result = await gen - print('got result from await:', result) except BaseException: advice = advisor.throw(*sys.exc_info()) else: From f63513f08a32460a84d38932a14b608ae2e827e5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ionel=20Cristian=20M=C4=83rie=C8=99?= Date: Thu, 11 Jun 2020 18:35:03 +0300 Subject: [PATCH 157/179] Style fix. --- tests/test_contrib.py | 1 + 1 file changed, 1 insertion(+) diff --git a/tests/test_contrib.py b/tests/test_contrib.py index 9eedebe..5eb0373 100644 --- a/tests/test_contrib.py +++ b/tests/test_contrib.py @@ -15,6 +15,7 @@ def flaky_func(arg): def test_done_suceess(): calls = [] + @retry def ok_func(): calls.append(1) From 0888d19c241aaca5484fecaf28a48b78463cc2c5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ionel=20Cristian=20M=C4=83rie=C8=99?= Date: Thu, 11 Jun 2020 19:09:01 +0300 Subject: [PATCH 158/179] Update changelog. --- CHANGELOG.rst | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/CHANGELOG.rst b/CHANGELOG.rst index 1388d9a..c12fcc7 100644 --- a/CHANGELOG.rst +++ b/CHANGELOG.rst @@ -1,6 +1,11 @@ Changelog ========= +1.5.1 (2020-06-11) +------------------ + +* Remove some debug leftover prints from `v1.5.0`. + 1.5.0 (2020-04-05) ------------------ From 666ee464d237f03a86ebe63bba3d89a7991c311a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ionel=20Cristian=20M=C4=83rie=C8=99?= Date: Thu, 11 Jun 2020 19:09:10 +0300 Subject: [PATCH 159/179] =?UTF-8?q?Bump=20version:=201.5.0=20=E2=86=92=201?= =?UTF-8?q?.5.1?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .bumpversion.cfg | 2 +- README.rst | 4 ++-- docs/conf.py | 2 +- setup.py | 2 +- src/aspectlib/__init__.py | 2 +- 5 files changed, 6 insertions(+), 6 deletions(-) diff --git a/.bumpversion.cfg b/.bumpversion.cfg index 0660c61..71cc681 100644 --- a/.bumpversion.cfg +++ b/.bumpversion.cfg @@ -1,5 +1,5 @@ [bumpversion] -current_version = 1.5.0 +current_version = 1.5.1 commit = True tag = True diff --git a/README.rst b/README.rst index c397be2..950704f 100644 --- a/README.rst +++ b/README.rst @@ -55,9 +55,9 @@ Overview :alt: Supported implementations :target: https://pypi.org/project/aspectlib -.. |commits-since| image:: https://img.shields.io/github/commits-since/ionelmc/python-aspectlib/v1.5.0.svg +.. |commits-since| image:: https://img.shields.io/github/commits-since/ionelmc/python-aspectlib/v1.5.1.svg :alt: Commits since latest release - :target: https://github.com/ionelmc/python-aspectlib/compare/v1.5.0...master + :target: https://github.com/ionelmc/python-aspectlib/compare/v1.5.1...master diff --git a/docs/conf.py b/docs/conf.py index 4b7690c..7b9b64b 100644 --- a/docs/conf.py +++ b/docs/conf.py @@ -20,7 +20,7 @@ year = '2014-2020' author = 'Ionel Cristian Mărieș' copyright = '{0}, {1}'.format(year, author) -version = release = '1.5.0' +version = release = '1.5.1' pygments_style = 'trac' templates_path = ['.'] diff --git a/setup.py b/setup.py index 6846710..58aca1f 100755 --- a/setup.py +++ b/setup.py @@ -25,7 +25,7 @@ def read(*names, **kwargs): setup( name='aspectlib', - version='1.5.0', + version='1.5.1', license='BSD-2-Clause', description='``aspectlib`` is an aspect-oriented programming, monkey-patch and decorators library. It is useful when changing', long_description='%s\n%s' % ( diff --git a/src/aspectlib/__init__.py b/src/aspectlib/__init__.py index d5f1bff..defd9d2 100644 --- a/src/aspectlib/__init__.py +++ b/src/aspectlib/__init__.py @@ -57,7 +57,7 @@ def isasyncfunction(obj): isasyncfunction = None __all__ = 'weave', 'Aspect', 'Proceed', 'Return', 'ALL_METHODS', 'NORMAL_METHODS', 'ABSOLUTELY_ALL_METHODS' -__version__ = '1.5.0' +__version__ = '1.5.1' logger = getLogger(__name__) logdebug = logf(logger.debug) From 75504294b2024935d6a65ca63ca1ff71b71250c6 Mon Sep 17 00:00:00 2001 From: Felix Yan Date: Sat, 14 Nov 2020 04:21:27 +0800 Subject: [PATCH 160/179] Fix test for Python 3.9 Python 3.9 has neither dummy_thread nor _dummy_thread anymore. Using _thread.allocate_lock passes all tests here. --- src/aspectlib/test.py | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/src/aspectlib/test.py b/src/aspectlib/test.py index 5146f6c..e8c5464 100644 --- a/src/aspectlib/test.py +++ b/src/aspectlib/test.py @@ -29,7 +29,10 @@ try: from dummy_thread import allocate_lock except ImportError: - from _dummy_thread import allocate_lock + try: + from _dummy_thread import allocate_lock + except ImportError: + from _thread import allocate_lock try: from collections import OrderedDict except ImportError: From 117dfdd738132c0f7a5d134de46d5dceb745d92b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ionel=20Cristian=20M=C4=83rie=C8=99?= Date: Sun, 15 Nov 2020 12:55:40 +0200 Subject: [PATCH 161/179] Update project skel (include py 3.9 in CI). --- .appveyor.yml | 45 ++++++++++++++++++++++++++++++++++++++ .cookiecutterrc | 9 +++----- .editorconfig | 6 ++++- .gitignore | 5 ++++- .pre-commit-config.yaml | 3 ++- .travis.yml | 22 ++++++++++++++----- CONTRIBUTING.rst | 6 ++--- ci/requirements.txt | 2 +- ci/templates/.appveyor.yml | 1 + ci/templates/.travis.yml | 9 ++++---- ci/templates/tox.ini | 5 ++--- docs/requirements.txt | 3 +-- setup.cfg | 6 ++--- setup.py | 1 + tox.ini | 41 +++++++++++++++++++++++++++++++--- 15 files changed, 130 insertions(+), 34 deletions(-) diff --git a/.appveyor.yml b/.appveyor.yml index 970b5a3..b9054e6 100644 --- a/.appveyor.yml +++ b/.appveyor.yml @@ -1,5 +1,6 @@ version: '{branch}-{build}' build: off +image: Visual Studio 2019 environment: global: COVERALLS_EXTRAS: '-v' @@ -234,6 +235,50 @@ environment: PYTHON_VERSION: '3.8' PYTHON_ARCH: '64' WHEEL_PATH: .tox/dist + - TOXENV: py39-cover,codecov,coveralls + TOXPYTHON: C:\Python39\python.exe + PYTHON_HOME: C:\Python39 + PYTHON_VERSION: '3.9' + PYTHON_ARCH: '32' + - TOXENV: py39-cover,codecov,coveralls + TOXPYTHON: C:\Python39-x64\python.exe + PYTHON_HOME: C:\Python39-x64 + PYTHON_VERSION: '3.9' + PYTHON_ARCH: '64' + - TOXENV: py39-cover-debug,codecov,coveralls + TOXPYTHON: C:\Python39\python.exe + PYTHON_HOME: C:\Python39 + PYTHON_VERSION: '3.9' + PYTHON_ARCH: '32' + - TOXENV: py39-cover-debug,codecov,coveralls + TOXPYTHON: C:\Python39-x64\python.exe + PYTHON_HOME: C:\Python39-x64 + PYTHON_VERSION: '3.9' + PYTHON_ARCH: '64' + - TOXENV: py39-nocov,codecov,coveralls + TOXPYTHON: C:\Python39\python.exe + PYTHON_HOME: C:\Python39 + PYTHON_VERSION: '3.9' + PYTHON_ARCH: '32' + WHEEL_PATH: .tox/dist + - TOXENV: py39-nocov,codecov,coveralls + TOXPYTHON: C:\Python39-x64\python.exe + PYTHON_HOME: C:\Python39-x64 + PYTHON_VERSION: '3.9' + PYTHON_ARCH: '64' + WHEEL_PATH: .tox/dist + - TOXENV: py39-nocov-debug,codecov,coveralls + TOXPYTHON: C:\Python39\python.exe + PYTHON_HOME: C:\Python39 + PYTHON_VERSION: '3.9' + PYTHON_ARCH: '32' + WHEEL_PATH: .tox/dist + - TOXENV: py39-nocov-debug,codecov,coveralls + TOXPYTHON: C:\Python39-x64\python.exe + PYTHON_HOME: C:\Python39-x64 + PYTHON_VERSION: '3.9' + PYTHON_ARCH: '64' + WHEEL_PATH: .tox/dist init: - ps: echo $env:TOXENV - ps: ls C:\Python* diff --git a/.cookiecutterrc b/.cookiecutterrc index 113ace3..48358a2 100644 --- a/.cookiecutterrc +++ b/.cookiecutterrc @@ -1,9 +1,6 @@ # Generated by cookiepatcher, a small shim around cookiecutter (pip install cookiepatcher) -cookiecutter: - _extensions: - - jinja2_time.TimeExtension - _template: /home/ionel/open-source/cookiecutter-pylibrary +default_context: allow_tests_inside_package: no appveyor: yes c_extension_function: '-' @@ -32,7 +29,7 @@ cookiecutter: project_short_description: '``aspectlib`` is an aspect-oriented programming, monkey-patch and decorators library. It is useful when changing' pypi_badge: yes pypi_disable_upload: no - release_date: '2016-05-10' + release_date: '2020-06-11' repo_hosting: github.com repo_hosting_domain: github.com repo_name: python-aspectlib @@ -50,7 +47,7 @@ cookiecutter: test_runner: pytest travis: yes travis_osx: no - version: 1.4.2 + version: 1.5.1 website: http://blog.ionelmc.ro year_from: '2014' year_to: '2020' diff --git a/.editorconfig b/.editorconfig index a9c7977..586c736 100644 --- a/.editorconfig +++ b/.editorconfig @@ -2,10 +2,11 @@ root = true [*] +# Use Unix-style newlines for most files (except Windows files, see below). end_of_line = lf trim_trailing_whitespace = true -insert_final_newline = true indent_style = space +insert_final_newline = true indent_size = 4 charset = utf-8 @@ -14,3 +15,6 @@ end_of_line = crlf [*.{yml,yaml}] indent_size = 2 + +[*.tsv] +indent_style = tab diff --git a/.gitignore b/.gitignore index dfe5838..83a43fd 100644 --- a/.gitignore +++ b/.gitignore @@ -39,11 +39,14 @@ htmlcov # Translations *.mo -# Mr Developer +# Buildout .mr.developer.cfg + +# IDE project files .project .pydevproject .idea +.vscode *.iml *.komodoproject diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index e0946b3..6e974cb 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -2,13 +2,14 @@ # pre-commit install # To update the pre-commit hooks run: # pre-commit install-hooks -exclude: '^(.tox|ci/templates|.bumpversion.cfg)(/|$)' +exclude: '^(\.tox|ci/templates|\.bumpversion\.cfg)(/|$)' repos: - repo: https://github.com/pre-commit/pre-commit-hooks rev: master hooks: - id: trailing-whitespace - id: end-of-file-fixer + - id: debug-statements - repo: https://github.com/timothycrosley/isort rev: master hooks: diff --git a/.travis.yml b/.travis.yml index 9a50bbc..4df3c8f 100644 --- a/.travis.yml +++ b/.travis.yml @@ -1,10 +1,12 @@ language: python dist: xenial +virt: lxd cache: false env: global: - LD_PRELOAD=/lib/x86_64-linux-gnu/libSegFault.so - SEGFAULT_SIGNALS=all + - LANG=en_US.UTF-8 matrix: include: - python: '3.6' @@ -73,6 +75,18 @@ matrix: - env: - TOXENV=py38-nocov-debug python: '3.8' + - env: + - TOXENV=py39-cover,codecov,coveralls + python: '3.9' + - env: + - TOXENV=py39-cover-debug,codecov,coveralls + python: '3.9' + - env: + - TOXENV=py39-nocov + python: '3.9' + - env: + - TOXENV=py39-nocov-debug + python: '3.9' - env: - TOXENV=pypy-cover,codecov,coveralls python: 'pypy' @@ -87,19 +101,15 @@ matrix: python: 'pypy' - env: - TOXENV=pypy3-cover,codecov,coveralls - - TOXPYTHON=pypy3 python: 'pypy3' - env: - TOXENV=pypy3-cover-debug,codecov,coveralls - - TOXPYTHON=pypy3 python: 'pypy3' - env: - TOXENV=pypy3-nocov - - TOXPYTHON=pypy3 python: 'pypy3' - env: - TOXENV=pypy3-nocov-debug - - TOXPYTHON=pypy3 python: 'pypy3' before_install: - python --version @@ -114,8 +124,8 @@ install: script: - tox -v after_failure: - - more .tox/log/* | cat - - more .tox/*/log/* | cat + - cat .tox/log/* + - cat .tox/*/log/* notifications: email: on_success: never diff --git a/CONTRIBUTING.rst b/CONTRIBUTING.rst index 9b57832..a27c87f 100644 --- a/CONTRIBUTING.rst +++ b/CONTRIBUTING.rst @@ -41,7 +41,7 @@ To set up `python-aspectlib` for local development: (look for the "Fork" button). 2. Clone your fork locally:: - git clone git@github.com:ionelmc/python-aspectlib.git + git clone git@github.com:YOURGITHUBNAME/python-aspectlib.git 3. Create a branch for local development:: @@ -85,6 +85,6 @@ To run a subset of tests:: tox -e envname -- pytest -k test_myfeature -To run all the test environments in *parallel* (you need to ``pip install detox``):: +To run all the test environments in *parallel*:: - detox + tox -p auto diff --git a/ci/requirements.txt b/ci/requirements.txt index b2a21e5..d7f5177 100644 --- a/ci/requirements.txt +++ b/ci/requirements.txt @@ -1,4 +1,4 @@ virtualenv>=16.6.0 pip>=19.1.1 setuptools>=18.0.1 -six>=1.12.0 +six>=1.14.0 diff --git a/ci/templates/.appveyor.yml b/ci/templates/.appveyor.yml index 4c6502e..00704b9 100644 --- a/ci/templates/.appveyor.yml +++ b/ci/templates/.appveyor.yml @@ -1,5 +1,6 @@ version: '{branch}-{build}' build: off +image: Visual Studio 2019 environment: global: COVERALLS_EXTRAS: '-v' diff --git a/ci/templates/.travis.yml b/ci/templates/.travis.yml index 84171cd..5f3e48a 100644 --- a/ci/templates/.travis.yml +++ b/ci/templates/.travis.yml @@ -1,10 +1,12 @@ language: python dist: xenial +virt: lxd cache: false env: global: - LD_PRELOAD=/lib/x86_64-linux-gnu/libSegFault.so - SEGFAULT_SIGNALS=all + - LANG=en_US.UTF-8 matrix: include: - python: '3.6' @@ -17,13 +19,12 @@ matrix: - env: - TOXENV={{ env }}{% if config.cover %},codecov,coveralls{% endif %} {%- if env.startswith('pypy3') %}{{ '' }} - - TOXPYTHON=pypy3 python: 'pypy3' {%- elif env.startswith('pypy') %}{{ '' }} python: 'pypy' {%- else %}{{ '' }} python: '{{ '{0[2]}.{0[3]}'.format(env) }}' -{%- endif %} +{%- endif %}{{ '' }} {%- endfor %}{{ '' }} before_install: - python --version @@ -38,8 +39,8 @@ install: script: - tox -v after_failure: - - more .tox/log/* | cat - - more .tox/*/log/* | cat + - cat .tox/log/* + - cat .tox/*/log/* notifications: email: on_success: never diff --git a/ci/templates/tox.ini b/ci/templates/tox.ini index 338859f..eaef583 100644 --- a/ci/templates/tox.ini +++ b/ci/templates/tox.ini @@ -10,8 +10,7 @@ envlist = [testenv] basepython = - docs: {env:TOXPYTHON:python3.6} - {bootstrap,clean,check,report,codecov,coveralls}: {env:TOXPYTHON:python3} + {bootstrap,clean,check,report,docs,codecov,coveralls}: {env:TOXPYTHON:python3} setenv = PYTHONPATH={toxinidir}/tests PYTHONUNBUFFERED=yes @@ -52,7 +51,7 @@ commands = python setup.py check --strict --metadata --restructuredtext check-manifest {toxinidir} flake8 - isort --verbose --check-only --diff --recursive + isort --verbose --check-only --diff --filter-files . [testenv:docs] diff --git a/docs/requirements.txt b/docs/requirements.txt index f53d26e..62bc14e 100644 --- a/docs/requirements.txt +++ b/docs/requirements.txt @@ -1,3 +1,2 @@ -sphinx +sphinx>=1.3 sphinx-py3doc-enhanced-theme --e . diff --git a/setup.cfg b/setup.cfg index 14d07c4..4e608d1 100644 --- a/setup.cfg +++ b/setup.cfg @@ -3,7 +3,7 @@ universal = 1 [flake8] max-line-length = 140 -exclude = .tox,ci/templates +exclude = .tox,.eggs,ci/templates,build,dist [tool:pytest] # If a pytest section is found in one of the possible config files @@ -36,8 +36,7 @@ line_length = 120 known_first_party = aspectlib default_section = THIRDPARTY forced_separate = test_aspectlib -not_skip = __init__.py -skip = .tox,ci/templates +skip = .tox,.eggs,ci/templates,build,dist [matrix] # This is the configuration for the `./bootstrap.py` script. @@ -66,6 +65,7 @@ python_versions = py36 py37 py38 + py39 pypy pypy3 diff --git a/setup.py b/setup.py index 58aca1f..db77bd8 100755 --- a/setup.py +++ b/setup.py @@ -55,6 +55,7 @@ def read(*names, **kwargs): 'Programming Language :: Python :: 3.6', 'Programming Language :: Python :: 3.7', 'Programming Language :: Python :: 3.8', + 'Programming Language :: Python :: 3.9', 'Programming Language :: Python :: Implementation :: CPython', 'Programming Language :: Python :: Implementation :: PyPy', # uncomment if you test on these interpreters: diff --git a/tox.ini b/tox.ini index 86f64f6..8c607de 100644 --- a/tox.ini +++ b/tox.ini @@ -23,6 +23,10 @@ envlist = py38-cover-debug, py38-nocov, py38-nocov-debug, + py39-cover, + py39-cover-debug, + py39-nocov, + py39-nocov-debug, pypy-cover, pypy-cover-debug, pypy-nocov, @@ -35,8 +39,7 @@ envlist = [testenv] basepython = - docs: {env:TOXPYTHON:python3.6} - {bootstrap,clean,check,report,codecov,coveralls}: {env:TOXPYTHON:python3} + {bootstrap,clean,check,report,docs,codecov,coveralls}: {env:TOXPYTHON:python3} setenv = PYTHONPATH={toxinidir}/tests PYTHONUNBUFFERED=yes @@ -77,7 +80,7 @@ commands = python setup.py check --strict --metadata --restructuredtext check-manifest {toxinidir} flake8 - isort --verbose --check-only --diff --recursive + isort --verbose --check-only --diff --filter-files . [testenv:docs] @@ -285,6 +288,38 @@ setenv = {[testenv]setenv} ASPECTLIB_DEBUG=yes +[testenv:py39-cover] +basepython = {env:TOXPYTHON:python3.9} +setenv = + {[testenv]setenv} +usedevelop = true +commands = + {posargs:pytest --cov --cov-report=term-missing -vv} +deps = + {[testenv]deps} + pytest-cov + +[testenv:py39-cover-debug] +basepython = {env:TOXPYTHON:python3.9} +setenv = + {[testenv]setenv} + ASPECTLIB_DEBUG=yes +usedevelop = true +commands = + {posargs:pytest --cov --cov-report=term-missing -vv} +deps = + {[testenv]deps} + pytest-cov + +[testenv:py39-nocov] +basepython = {env:TOXPYTHON:python3.9} + +[testenv:py39-nocov-debug] +basepython = {env:TOXPYTHON:python3.9} +setenv = + {[testenv]setenv} + ASPECTLIB_DEBUG=yes + [testenv:pypy-cover] basepython = {env:TOXPYTHON:pypy} setenv = From 8fae25b1dd8c60f9be0aae3f1d2f11bd2c5d12c7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ionel=20Cristian=20M=C4=83rie=C8=99?= Date: Sun, 15 Nov 2020 12:58:01 +0200 Subject: [PATCH 162/179] Update changelog. --- AUTHORS.rst | 3 ++- CHANGELOG.rst | 6 ++++++ 2 files changed, 8 insertions(+), 1 deletion(-) diff --git a/AUTHORS.rst b/AUTHORS.rst index 24b749f..b290b69 100644 --- a/AUTHORS.rst +++ b/AUTHORS.rst @@ -3,4 +3,5 @@ Authors ======= * Ionel Cristian Mărieș - https://blog.ionelmc.ro -* Jonas Maurus - https://github.com/jdelic/ +* Jonas Maurus - https://github.com/jdelic +* Felix Yan - https://github.com/felixonmars diff --git a/CHANGELOG.rst b/CHANGELOG.rst index c12fcc7..163e7e5 100644 --- a/CHANGELOG.rst +++ b/CHANGELOG.rst @@ -1,6 +1,12 @@ Changelog ========= +1.5.2 (2020-11-15) +------------------ + +* Fixed broken import on Python 3.9. + Contributed by Felix Yan in `#19 `_. + 1.5.1 (2020-06-11) ------------------ From 889055f773581872b71469779b1fa0d5a539098f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ionel=20Cristian=20M=C4=83rie=C8=99?= Date: Sun, 15 Nov 2020 13:35:52 +0200 Subject: [PATCH 163/179] Fix pypy3 CI. --- .travis.yml | 4 ++++ ci/templates/.travis.yml | 1 + 2 files changed, 5 insertions(+) diff --git a/.travis.yml b/.travis.yml index 4df3c8f..120712f 100644 --- a/.travis.yml +++ b/.travis.yml @@ -101,15 +101,19 @@ matrix: python: 'pypy' - env: - TOXENV=pypy3-cover,codecov,coveralls + - TOXPYTHON=pypy3 python: 'pypy3' - env: - TOXENV=pypy3-cover-debug,codecov,coveralls + - TOXPYTHON=pypy3 python: 'pypy3' - env: - TOXENV=pypy3-nocov + - TOXPYTHON=pypy3 python: 'pypy3' - env: - TOXENV=pypy3-nocov-debug + - TOXPYTHON=pypy3 python: 'pypy3' before_install: - python --version diff --git a/ci/templates/.travis.yml b/ci/templates/.travis.yml index 5f3e48a..2b41139 100644 --- a/ci/templates/.travis.yml +++ b/ci/templates/.travis.yml @@ -19,6 +19,7 @@ matrix: - env: - TOXENV={{ env }}{% if config.cover %},codecov,coveralls{% endif %} {%- if env.startswith('pypy3') %}{{ '' }} + - TOXPYTHON=pypy3 python: 'pypy3' {%- elif env.startswith('pypy') %}{{ '' }} python: 'pypy' From 483fd5c3aeb285a2ee35e6ee059168e8b13da184 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ionel=20Cristian=20M=C4=83rie=C8=99?= Date: Sun, 15 Nov 2020 21:28:49 +0200 Subject: [PATCH 164/179] =?UTF-8?q?Bump=20version:=201.5.1=20=E2=86=92=201?= =?UTF-8?q?.5.2?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .bumpversion.cfg | 2 +- README.rst | 4 ++-- docs/conf.py | 2 +- setup.py | 2 +- src/aspectlib/__init__.py | 2 +- 5 files changed, 6 insertions(+), 6 deletions(-) diff --git a/.bumpversion.cfg b/.bumpversion.cfg index 71cc681..60faa92 100644 --- a/.bumpversion.cfg +++ b/.bumpversion.cfg @@ -1,5 +1,5 @@ [bumpversion] -current_version = 1.5.1 +current_version = 1.5.2 commit = True tag = True diff --git a/README.rst b/README.rst index 950704f..c6bf3a6 100644 --- a/README.rst +++ b/README.rst @@ -55,9 +55,9 @@ Overview :alt: Supported implementations :target: https://pypi.org/project/aspectlib -.. |commits-since| image:: https://img.shields.io/github/commits-since/ionelmc/python-aspectlib/v1.5.1.svg +.. |commits-since| image:: https://img.shields.io/github/commits-since/ionelmc/python-aspectlib/v1.5.2.svg :alt: Commits since latest release - :target: https://github.com/ionelmc/python-aspectlib/compare/v1.5.1...master + :target: https://github.com/ionelmc/python-aspectlib/compare/v1.5.2...master diff --git a/docs/conf.py b/docs/conf.py index 7b9b64b..51c53db 100644 --- a/docs/conf.py +++ b/docs/conf.py @@ -20,7 +20,7 @@ year = '2014-2020' author = 'Ionel Cristian Mărieș' copyright = '{0}, {1}'.format(year, author) -version = release = '1.5.1' +version = release = '1.5.2' pygments_style = 'trac' templates_path = ['.'] diff --git a/setup.py b/setup.py index db77bd8..e4f6d12 100755 --- a/setup.py +++ b/setup.py @@ -25,7 +25,7 @@ def read(*names, **kwargs): setup( name='aspectlib', - version='1.5.1', + version='1.5.2', license='BSD-2-Clause', description='``aspectlib`` is an aspect-oriented programming, monkey-patch and decorators library. It is useful when changing', long_description='%s\n%s' % ( diff --git a/src/aspectlib/__init__.py b/src/aspectlib/__init__.py index defd9d2..b85f1dd 100644 --- a/src/aspectlib/__init__.py +++ b/src/aspectlib/__init__.py @@ -57,7 +57,7 @@ def isasyncfunction(obj): isasyncfunction = None __all__ = 'weave', 'Aspect', 'Proceed', 'Return', 'ALL_METHODS', 'NORMAL_METHODS', 'ABSOLUTELY_ALL_METHODS' -__version__ = '1.5.1' +__version__ = '1.5.2' logger = getLogger(__name__) logdebug = logf(logger.debug) From 24e6e276896a764e7b1888e6e9cc783a50d98ca1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ionel=20Cristian=20M=C4=83rie=C8=99?= Date: Wed, 17 Mar 2021 18:02:04 +0200 Subject: [PATCH 165/179] Update skel and drop 3.5 --- .appveyor.yml | 44 -------------------------------------------- .bumpversion.cfg | 5 ++--- .cookiecutterrc | 8 ++++---- .travis.yml | 12 ------------ CHANGELOG.rst | 1 + CONTRIBUTING.rst | 3 ++- LICENSE | 2 +- README.rst | 4 ++-- docs/conf.py | 2 +- setup.cfg | 1 - setup.py | 3 +-- tox.ini | 36 ------------------------------------ 12 files changed, 14 insertions(+), 107 deletions(-) diff --git a/.appveyor.yml b/.appveyor.yml index b9054e6..83262dd 100644 --- a/.appveyor.yml +++ b/.appveyor.yml @@ -59,50 +59,6 @@ environment: PYTHON_ARCH: '64' WHEEL_PATH: .tox/dist WINDOWS_SDK_VERSION: v7.0 - - TOXENV: py35-cover,codecov,coveralls - TOXPYTHON: C:\Python35\python.exe - PYTHON_HOME: C:\Python35 - PYTHON_VERSION: '3.5' - PYTHON_ARCH: '32' - - TOXENV: py35-cover,codecov,coveralls - TOXPYTHON: C:\Python35-x64\python.exe - PYTHON_HOME: C:\Python35-x64 - PYTHON_VERSION: '3.5' - PYTHON_ARCH: '64' - - TOXENV: py35-cover-debug,codecov,coveralls - TOXPYTHON: C:\Python35\python.exe - PYTHON_HOME: C:\Python35 - PYTHON_VERSION: '3.5' - PYTHON_ARCH: '32' - - TOXENV: py35-cover-debug,codecov,coveralls - TOXPYTHON: C:\Python35-x64\python.exe - PYTHON_HOME: C:\Python35-x64 - PYTHON_VERSION: '3.5' - PYTHON_ARCH: '64' - - TOXENV: py35-nocov,codecov,coveralls - TOXPYTHON: C:\Python35\python.exe - PYTHON_HOME: C:\Python35 - PYTHON_VERSION: '3.5' - PYTHON_ARCH: '32' - WHEEL_PATH: .tox/dist - - TOXENV: py35-nocov,codecov,coveralls - TOXPYTHON: C:\Python35-x64\python.exe - PYTHON_HOME: C:\Python35-x64 - PYTHON_VERSION: '3.5' - PYTHON_ARCH: '64' - WHEEL_PATH: .tox/dist - - TOXENV: py35-nocov-debug,codecov,coveralls - TOXPYTHON: C:\Python35\python.exe - PYTHON_HOME: C:\Python35 - PYTHON_VERSION: '3.5' - PYTHON_ARCH: '32' - WHEEL_PATH: .tox/dist - - TOXENV: py35-nocov-debug,codecov,coveralls - TOXPYTHON: C:\Python35-x64\python.exe - PYTHON_HOME: C:\Python35-x64 - PYTHON_VERSION: '3.5' - PYTHON_ARCH: '64' - WHEEL_PATH: .tox/dist - TOXENV: py36-cover,codecov,coveralls TOXPYTHON: C:\Python36\python.exe PYTHON_HOME: C:\Python36 diff --git a/.bumpversion.cfg b/.bumpversion.cfg index 60faa92..9ca0bcb 100644 --- a/.bumpversion.cfg +++ b/.bumpversion.cfg @@ -8,8 +8,8 @@ search = version='{current_version}' replace = version='{new_version}' [bumpversion:file:README.rst] -search = v{current_version}. -replace = v{new_version}. +search = /v{current_version}.svg +replace = /v{new_version}.svg [bumpversion:file:docs/conf.py] search = version = release = '{current_version}' @@ -18,4 +18,3 @@ replace = version = release = '{new_version}' [bumpversion:file:src/aspectlib/__init__.py] search = __version__ = '{current_version}' replace = __version__ = '{new_version}' - diff --git a/.cookiecutterrc b/.cookiecutterrc index 48358a2..41c2e1e 100644 --- a/.cookiecutterrc +++ b/.cookiecutterrc @@ -20,7 +20,7 @@ default_context: distribution_name: aspectlib email: contact@ionelmc.ro full_name: Ionel Cristian Mărieș - landscape: no + legacy_python: yes license: BSD 2-Clause License linter: flake8 package_name: aspectlib @@ -29,7 +29,7 @@ default_context: project_short_description: '``aspectlib`` is an aspect-oriented programming, monkey-patch and decorators library. It is useful when changing' pypi_badge: yes pypi_disable_upload: no - release_date: '2020-06-11' + release_date: '2020-11-15' repo_hosting: github.com repo_hosting_domain: github.com repo_name: python-aspectlib @@ -47,7 +47,7 @@ default_context: test_runner: pytest travis: yes travis_osx: no - version: 1.5.1 + version: 1.5.2 website: http://blog.ionelmc.ro year_from: '2014' - year_to: '2020' + year_to: '2021' diff --git a/.travis.yml b/.travis.yml index 120712f..86af895 100644 --- a/.travis.yml +++ b/.travis.yml @@ -27,18 +27,6 @@ matrix: - env: - TOXENV=py27-nocov-debug python: '2.7' - - env: - - TOXENV=py35-cover,codecov,coveralls - python: '3.5' - - env: - - TOXENV=py35-cover-debug,codecov,coveralls - python: '3.5' - - env: - - TOXENV=py35-nocov - python: '3.5' - - env: - - TOXENV=py35-nocov-debug - python: '3.5' - env: - TOXENV=py36-cover,codecov,coveralls python: '3.6' diff --git a/CHANGELOG.rst b/CHANGELOG.rst index 163e7e5..c4de5e2 100644 --- a/CHANGELOG.rst +++ b/CHANGELOG.rst @@ -1,3 +1,4 @@ + Changelog ========= diff --git a/CONTRIBUTING.rst b/CONTRIBUTING.rst index a27c87f..87647a9 100644 --- a/CONTRIBUTING.rst +++ b/CONTRIBUTING.rst @@ -74,7 +74,8 @@ For merging, you should: 4. Add yourself to ``AUTHORS.rst``. .. [1] If you don't have all the necessary python versions available locally you can rely on Travis - it will - `run the tests `_ for each change you add in the pull request. + `run the tests `_ + for each change you add in the pull request. It will be slower though ... diff --git a/LICENSE b/LICENSE index 69350bf..176891a 100644 --- a/LICENSE +++ b/LICENSE @@ -1,6 +1,6 @@ BSD 2-Clause License -Copyright (c) 2014-2020, Ionel Cristian Mărieș. All rights reserved. +Copyright (c) 2014-2021, Ionel Cristian Mărieș. All rights reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: diff --git a/README.rst b/README.rst index c6bf3a6..2a8c89a 100644 --- a/README.rst +++ b/README.rst @@ -19,9 +19,9 @@ Overview :target: https://readthedocs.org/projects/python-aspectlib :alt: Documentation Status -.. |travis| image:: https://api.travis-ci.org/ionelmc/python-aspectlib.svg?branch=master +.. |travis| image:: https://api.travis-ci.com/ionelmc/python-aspectlib.svg?branch=master :alt: Travis-CI Build Status - :target: https://travis-ci.org/ionelmc/python-aspectlib + :target: https://travis-ci.com/github/ionelmc/python-aspectlib .. |appveyor| image:: https://ci.appveyor.com/api/projects/status/github/ionelmc/python-aspectlib?branch=master&svg=true :alt: AppVeyor Build Status diff --git a/docs/conf.py b/docs/conf.py index 51c53db..a0b738d 100644 --- a/docs/conf.py +++ b/docs/conf.py @@ -17,7 +17,7 @@ source_suffix = '.rst' master_doc = 'index' project = 'Aspectlib' -year = '2014-2020' +year = '2014-2021' author = 'Ionel Cristian Mărieș' copyright = '{0}, {1}'.format(year, author) version = release = '1.5.2' diff --git a/setup.cfg b/setup.cfg index 4e608d1..a4f0d99 100644 --- a/setup.cfg +++ b/setup.cfg @@ -61,7 +61,6 @@ skip = .tox,.eggs,ci/templates,build,dist python_versions = py27 - py35 py36 py37 py38 diff --git a/setup.py b/setup.py index e4f6d12..a9f305e 100755 --- a/setup.py +++ b/setup.py @@ -51,7 +51,6 @@ def read(*names, **kwargs): 'Programming Language :: Python', 'Programming Language :: Python :: 2.7', 'Programming Language :: Python :: 3', - 'Programming Language :: Python :: 3.5', 'Programming Language :: Python :: 3.6', 'Programming Language :: Python :: 3.7', 'Programming Language :: Python :: 3.8', @@ -74,7 +73,7 @@ def read(*names, **kwargs): 'tests', 'mock', 'capture', 'replay', 'capture-replay', 'debugging', 'patching', 'monkeypatching', 'record', 'recording', 'mocking', 'logger' ], - python_requires='>=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*', + python_requires='>=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*, !=3.5.*', install_requires=[ 'fields' ], diff --git a/tox.ini b/tox.ini index 8c607de..c9cbc83 100644 --- a/tox.ini +++ b/tox.ini @@ -7,10 +7,6 @@ envlist = py27-cover-debug, py27-nocov, py27-nocov-debug, - py35-cover, - py35-cover-debug, - py35-nocov, - py35-nocov-debug, py36-cover, py36-cover-debug, py36-nocov, @@ -160,38 +156,6 @@ deps = {[testenv]deps} trollius -[testenv:py35-cover] -basepython = {env:TOXPYTHON:python3.5} -setenv = - {[testenv]setenv} -usedevelop = true -commands = - {posargs:pytest --cov --cov-report=term-missing -vv} -deps = - {[testenv]deps} - pytest-cov - -[testenv:py35-cover-debug] -basepython = {env:TOXPYTHON:python3.5} -setenv = - {[testenv]setenv} - ASPECTLIB_DEBUG=yes -usedevelop = true -commands = - {posargs:pytest --cov --cov-report=term-missing -vv} -deps = - {[testenv]deps} - pytest-cov - -[testenv:py35-nocov] -basepython = {env:TOXPYTHON:python3.5} - -[testenv:py35-nocov-debug] -basepython = {env:TOXPYTHON:python3.5} -setenv = - {[testenv]setenv} - ASPECTLIB_DEBUG=yes - [testenv:py36-cover] basepython = {env:TOXPYTHON:python3.6} setenv = From 7dccb198dfb426f529b81a28a755f3c02f8b50cb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ionel=20Cristian=20M=C4=83rie=C8=99?= Date: Sun, 2 May 2021 09:50:43 +0300 Subject: [PATCH 166/179] Remove tornado<6 test constraint. Ref #15. --- tests/test_integrations_py3.py | 5 ++++- tox.ini | 3 ++- 2 files changed, 6 insertions(+), 2 deletions(-) diff --git a/tests/test_integrations_py3.py b/tests/test_integrations_py3.py index e025eac..596589d 100644 --- a/tests/test_integrations_py3.py +++ b/tests/test_integrations_py3.py @@ -36,7 +36,10 @@ def test_decorate_tornado_coroutine(): @gen.coroutine @debug.log(print_to=buf, module=False, stacktrace=2, result_repr=repr) def coro(): - yield gen.Task(loop.add_timeout, timedelta(microseconds=10)) + if hasattr(gen, 'Task'): + yield gen.Task(loop.add_timeout, timedelta(microseconds=10)) + else: + yield gen.sleep(0.01) return "result" loop = ioloop.IOLoop.current() diff --git a/tox.ini b/tox.ini index c9cbc83..1bd1d1c 100644 --- a/tox.ini +++ b/tox.ini @@ -51,7 +51,8 @@ deps = pytest-clarity pytest-cov pytest-travis-fold - tornado<6.0 + six + tornado commands = {posargs:pytest --cov --cov-report=term-missing -vv --ignore=src} From ed4b036b9e69e195226faab719c470f77ad11cc2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ionel=20Cristian=20M=C4=83rie=C8=99?= Date: Thu, 20 Oct 2022 18:56:56 +0300 Subject: [PATCH 167/179] Drop support for old pythons and switch to github actions. --- .appveyor.yml | 255 -------------- .bumpversion.cfg | 6 +- .cookiecutterrc | 58 ++-- .coveragerc | 6 +- .github/workflows/github-actions.yml | 209 ++++++++++++ .pre-commit-config.yaml | 10 +- .travis.yml | 124 ------- CONTRIBUTING.rst | 6 +- LICENSE | 2 +- MANIFEST.in | 10 +- README.rst | 29 +- ci/appveyor-with-compiler.cmd | 23 -- ci/bootstrap.py | 47 ++- ci/requirements.txt | 1 + ci/templates/.appveyor.yml | 53 --- .../.github/workflows/github-actions.yml | 63 ++++ ci/templates/.travis.yml | 48 --- ci/templates/tox.ini | 119 ------- conftest.py | 7 +- docs/conf.py | 10 +- docs/spelling_wordlist.txt | 3 + pyproject.toml | 10 + pytest.ini | 38 +++ setup.cfg | 70 ---- setup.py | 52 +-- src/aspectlib/__init__.py | 201 +++-------- src/aspectlib/py2chainmap.py | 106 ------ src/aspectlib/py2ordereddict.py | 128 ------- src/aspectlib/py35support.py | 4 +- src/aspectlib/py3support.py | 3 +- src/aspectlib/test.py | 235 +++++++------ src/aspectlib/utils.py | 37 +- tests/mymod.py | 3 - tests/test_aspectlib.py | 69 ++-- tests/test_aspectlib_debug.py | 84 +++-- tests/test_aspectlib_py23.py | 169 ---------- tests/test_aspectlib_py3.py | 2 +- tests/test_aspectlib_py37.py | 5 +- tests/test_aspectlib_test.py | 256 ++++++++------ tests/test_integrations.py | 80 +++-- tests/test_integrations_py2.py | 64 ---- tests/test_integrations_py3.py | 48 --- tests/test_integrations_py37.py | 23 -- tox.ini | 315 ++---------------- 44 files changed, 954 insertions(+), 2137 deletions(-) delete mode 100644 .appveyor.yml create mode 100644 .github/workflows/github-actions.yml delete mode 100644 .travis.yml delete mode 100644 ci/appveyor-with-compiler.cmd delete mode 100644 ci/templates/.appveyor.yml create mode 100644 ci/templates/.github/workflows/github-actions.yml delete mode 100644 ci/templates/.travis.yml delete mode 100644 ci/templates/tox.ini create mode 100644 pyproject.toml create mode 100644 pytest.ini delete mode 100644 src/aspectlib/py2chainmap.py delete mode 100644 src/aspectlib/py2ordereddict.py delete mode 100644 tests/test_aspectlib_py23.py delete mode 100644 tests/test_integrations_py2.py delete mode 100644 tests/test_integrations_py3.py delete mode 100644 tests/test_integrations_py37.py diff --git a/.appveyor.yml b/.appveyor.yml deleted file mode 100644 index 83262dd..0000000 --- a/.appveyor.yml +++ /dev/null @@ -1,255 +0,0 @@ -version: '{branch}-{build}' -build: off -image: Visual Studio 2019 -environment: - global: - COVERALLS_EXTRAS: '-v' - COVERALLS_REPO_TOKEN: mHSWktPkrw65WSlziG8NnK9m1xj2k1kOQ - matrix: - - TOXENV: check - TOXPYTHON: C:\Python36\python.exe - PYTHON_HOME: C:\Python36 - PYTHON_VERSION: '3.6' - PYTHON_ARCH: '32' - - TOXENV: py27-cover,codecov,coveralls - TOXPYTHON: C:\Python27\python.exe - PYTHON_HOME: C:\Python27 - PYTHON_VERSION: '2.7' - PYTHON_ARCH: '32' - - TOXENV: py27-cover,codecov,coveralls - TOXPYTHON: C:\Python27-x64\python.exe - PYTHON_HOME: C:\Python27-x64 - PYTHON_VERSION: '2.7' - PYTHON_ARCH: '64' - WINDOWS_SDK_VERSION: v7.0 - - TOXENV: py27-cover-debug,codecov,coveralls - TOXPYTHON: C:\Python27\python.exe - PYTHON_HOME: C:\Python27 - PYTHON_VERSION: '2.7' - PYTHON_ARCH: '32' - - TOXENV: py27-cover-debug,codecov,coveralls - TOXPYTHON: C:\Python27-x64\python.exe - PYTHON_HOME: C:\Python27-x64 - PYTHON_VERSION: '2.7' - PYTHON_ARCH: '64' - WINDOWS_SDK_VERSION: v7.0 - - TOXENV: py27-nocov,codecov,coveralls - TOXPYTHON: C:\Python27\python.exe - PYTHON_HOME: C:\Python27 - PYTHON_VERSION: '2.7' - PYTHON_ARCH: '32' - WHEEL_PATH: .tox/dist - - TOXENV: py27-nocov,codecov,coveralls - TOXPYTHON: C:\Python27-x64\python.exe - PYTHON_HOME: C:\Python27-x64 - PYTHON_VERSION: '2.7' - PYTHON_ARCH: '64' - WHEEL_PATH: .tox/dist - WINDOWS_SDK_VERSION: v7.0 - - TOXENV: py27-nocov-debug,codecov,coveralls - TOXPYTHON: C:\Python27\python.exe - PYTHON_HOME: C:\Python27 - PYTHON_VERSION: '2.7' - PYTHON_ARCH: '32' - WHEEL_PATH: .tox/dist - - TOXENV: py27-nocov-debug,codecov,coveralls - TOXPYTHON: C:\Python27-x64\python.exe - PYTHON_HOME: C:\Python27-x64 - PYTHON_VERSION: '2.7' - PYTHON_ARCH: '64' - WHEEL_PATH: .tox/dist - WINDOWS_SDK_VERSION: v7.0 - - TOXENV: py36-cover,codecov,coveralls - TOXPYTHON: C:\Python36\python.exe - PYTHON_HOME: C:\Python36 - PYTHON_VERSION: '3.6' - PYTHON_ARCH: '32' - - TOXENV: py36-cover,codecov,coveralls - TOXPYTHON: C:\Python36-x64\python.exe - PYTHON_HOME: C:\Python36-x64 - PYTHON_VERSION: '3.6' - PYTHON_ARCH: '64' - - TOXENV: py36-cover-debug,codecov,coveralls - TOXPYTHON: C:\Python36\python.exe - PYTHON_HOME: C:\Python36 - PYTHON_VERSION: '3.6' - PYTHON_ARCH: '32' - - TOXENV: py36-cover-debug,codecov,coveralls - TOXPYTHON: C:\Python36-x64\python.exe - PYTHON_HOME: C:\Python36-x64 - PYTHON_VERSION: '3.6' - PYTHON_ARCH: '64' - - TOXENV: py36-nocov,codecov,coveralls - TOXPYTHON: C:\Python36\python.exe - PYTHON_HOME: C:\Python36 - PYTHON_VERSION: '3.6' - PYTHON_ARCH: '32' - WHEEL_PATH: .tox/dist - - TOXENV: py36-nocov,codecov,coveralls - TOXPYTHON: C:\Python36-x64\python.exe - PYTHON_HOME: C:\Python36-x64 - PYTHON_VERSION: '3.6' - PYTHON_ARCH: '64' - WHEEL_PATH: .tox/dist - - TOXENV: py36-nocov-debug,codecov,coveralls - TOXPYTHON: C:\Python36\python.exe - PYTHON_HOME: C:\Python36 - PYTHON_VERSION: '3.6' - PYTHON_ARCH: '32' - WHEEL_PATH: .tox/dist - - TOXENV: py36-nocov-debug,codecov,coveralls - TOXPYTHON: C:\Python36-x64\python.exe - PYTHON_HOME: C:\Python36-x64 - PYTHON_VERSION: '3.6' - PYTHON_ARCH: '64' - WHEEL_PATH: .tox/dist - - TOXENV: py37-cover,codecov,coveralls - TOXPYTHON: C:\Python37\python.exe - PYTHON_HOME: C:\Python37 - PYTHON_VERSION: '3.7' - PYTHON_ARCH: '32' - - TOXENV: py37-cover,codecov,coveralls - TOXPYTHON: C:\Python37-x64\python.exe - PYTHON_HOME: C:\Python37-x64 - PYTHON_VERSION: '3.7' - PYTHON_ARCH: '64' - - TOXENV: py37-cover-debug,codecov,coveralls - TOXPYTHON: C:\Python37\python.exe - PYTHON_HOME: C:\Python37 - PYTHON_VERSION: '3.7' - PYTHON_ARCH: '32' - - TOXENV: py37-cover-debug,codecov,coveralls - TOXPYTHON: C:\Python37-x64\python.exe - PYTHON_HOME: C:\Python37-x64 - PYTHON_VERSION: '3.7' - PYTHON_ARCH: '64' - - TOXENV: py37-nocov,codecov,coveralls - TOXPYTHON: C:\Python37\python.exe - PYTHON_HOME: C:\Python37 - PYTHON_VERSION: '3.7' - PYTHON_ARCH: '32' - WHEEL_PATH: .tox/dist - - TOXENV: py37-nocov,codecov,coveralls - TOXPYTHON: C:\Python37-x64\python.exe - PYTHON_HOME: C:\Python37-x64 - PYTHON_VERSION: '3.7' - PYTHON_ARCH: '64' - WHEEL_PATH: .tox/dist - - TOXENV: py37-nocov-debug,codecov,coveralls - TOXPYTHON: C:\Python37\python.exe - PYTHON_HOME: C:\Python37 - PYTHON_VERSION: '3.7' - PYTHON_ARCH: '32' - WHEEL_PATH: .tox/dist - - TOXENV: py37-nocov-debug,codecov,coveralls - TOXPYTHON: C:\Python37-x64\python.exe - PYTHON_HOME: C:\Python37-x64 - PYTHON_VERSION: '3.7' - PYTHON_ARCH: '64' - WHEEL_PATH: .tox/dist - - TOXENV: py38-cover,codecov,coveralls - TOXPYTHON: C:\Python38\python.exe - PYTHON_HOME: C:\Python38 - PYTHON_VERSION: '3.8' - PYTHON_ARCH: '32' - - TOXENV: py38-cover,codecov,coveralls - TOXPYTHON: C:\Python38-x64\python.exe - PYTHON_HOME: C:\Python38-x64 - PYTHON_VERSION: '3.8' - PYTHON_ARCH: '64' - - TOXENV: py38-cover-debug,codecov,coveralls - TOXPYTHON: C:\Python38\python.exe - PYTHON_HOME: C:\Python38 - PYTHON_VERSION: '3.8' - PYTHON_ARCH: '32' - - TOXENV: py38-cover-debug,codecov,coveralls - TOXPYTHON: C:\Python38-x64\python.exe - PYTHON_HOME: C:\Python38-x64 - PYTHON_VERSION: '3.8' - PYTHON_ARCH: '64' - - TOXENV: py38-nocov,codecov,coveralls - TOXPYTHON: C:\Python38\python.exe - PYTHON_HOME: C:\Python38 - PYTHON_VERSION: '3.8' - PYTHON_ARCH: '32' - WHEEL_PATH: .tox/dist - - TOXENV: py38-nocov,codecov,coveralls - TOXPYTHON: C:\Python38-x64\python.exe - PYTHON_HOME: C:\Python38-x64 - PYTHON_VERSION: '3.8' - PYTHON_ARCH: '64' - WHEEL_PATH: .tox/dist - - TOXENV: py38-nocov-debug,codecov,coveralls - TOXPYTHON: C:\Python38\python.exe - PYTHON_HOME: C:\Python38 - PYTHON_VERSION: '3.8' - PYTHON_ARCH: '32' - WHEEL_PATH: .tox/dist - - TOXENV: py38-nocov-debug,codecov,coveralls - TOXPYTHON: C:\Python38-x64\python.exe - PYTHON_HOME: C:\Python38-x64 - PYTHON_VERSION: '3.8' - PYTHON_ARCH: '64' - WHEEL_PATH: .tox/dist - - TOXENV: py39-cover,codecov,coveralls - TOXPYTHON: C:\Python39\python.exe - PYTHON_HOME: C:\Python39 - PYTHON_VERSION: '3.9' - PYTHON_ARCH: '32' - - TOXENV: py39-cover,codecov,coveralls - TOXPYTHON: C:\Python39-x64\python.exe - PYTHON_HOME: C:\Python39-x64 - PYTHON_VERSION: '3.9' - PYTHON_ARCH: '64' - - TOXENV: py39-cover-debug,codecov,coveralls - TOXPYTHON: C:\Python39\python.exe - PYTHON_HOME: C:\Python39 - PYTHON_VERSION: '3.9' - PYTHON_ARCH: '32' - - TOXENV: py39-cover-debug,codecov,coveralls - TOXPYTHON: C:\Python39-x64\python.exe - PYTHON_HOME: C:\Python39-x64 - PYTHON_VERSION: '3.9' - PYTHON_ARCH: '64' - - TOXENV: py39-nocov,codecov,coveralls - TOXPYTHON: C:\Python39\python.exe - PYTHON_HOME: C:\Python39 - PYTHON_VERSION: '3.9' - PYTHON_ARCH: '32' - WHEEL_PATH: .tox/dist - - TOXENV: py39-nocov,codecov,coveralls - TOXPYTHON: C:\Python39-x64\python.exe - PYTHON_HOME: C:\Python39-x64 - PYTHON_VERSION: '3.9' - PYTHON_ARCH: '64' - WHEEL_PATH: .tox/dist - - TOXENV: py39-nocov-debug,codecov,coveralls - TOXPYTHON: C:\Python39\python.exe - PYTHON_HOME: C:\Python39 - PYTHON_VERSION: '3.9' - PYTHON_ARCH: '32' - WHEEL_PATH: .tox/dist - - TOXENV: py39-nocov-debug,codecov,coveralls - TOXPYTHON: C:\Python39-x64\python.exe - PYTHON_HOME: C:\Python39-x64 - PYTHON_VERSION: '3.9' - PYTHON_ARCH: '64' - WHEEL_PATH: .tox/dist -init: - - ps: echo $env:TOXENV - - ps: ls C:\Python* -install: - - '%PYTHON_HOME%\python -mpip install --progress-bar=off tox -rci/requirements.txt' - - '%PYTHON_HOME%\Scripts\virtualenv --version' - - '%PYTHON_HOME%\Scripts\easy_install --version' - - '%PYTHON_HOME%\Scripts\pip --version' - - '%PYTHON_HOME%\Scripts\tox --version' -test_script: - - cmd /E:ON /V:ON /C .\ci\appveyor-with-compiler.cmd %PYTHON_HOME%\Scripts\tox -on_failure: - - ps: dir "env:" - - ps: get-content .tox\*\log\* - -### To enable remote debugging uncomment this (also, see: http://www.appveyor.com/docs/how-to/rdp-to-build-worker): -# on_finish: -# - ps: $blockRdp = $true; iex ((new-object net.webclient).DownloadString('https://raw.githubusercontent.com/appveyor/ci/master/scripts/enable-rdp.ps1')) diff --git a/.bumpversion.cfg b/.bumpversion.cfg index 9ca0bcb..8f991ed 100644 --- a/.bumpversion.cfg +++ b/.bumpversion.cfg @@ -7,10 +7,14 @@ tag = True search = version='{current_version}' replace = version='{new_version}' -[bumpversion:file:README.rst] +[bumpversion:file (badge):README.rst] search = /v{current_version}.svg replace = /v{new_version}.svg +[bumpversion:file (link):README.rst] +search = /v{current_version}...main +replace = /v{new_version}...main + [bumpversion:file:docs/conf.py] search = version = release = '{current_version}' replace = version = release = '{new_version}' diff --git a/.cookiecutterrc b/.cookiecutterrc index 41c2e1e..2351690 100644 --- a/.cookiecutterrc +++ b/.cookiecutterrc @@ -1,53 +1,57 @@ # Generated by cookiepatcher, a small shim around cookiecutter (pip install cookiepatcher) default_context: - allow_tests_inside_package: no - appveyor: yes + allow_tests_inside_package: 'no' + appveyor: 'no' c_extension_function: '-' c_extension_module: '-' - c_extension_optional: no - c_extension_support: no - c_extension_test_pypi: no + c_extension_optional: 'no' + c_extension_support: 'no' + c_extension_test_pypi: 'no' c_extension_test_pypi_username: ionelmc - codacy: no + codacy: 'no' codacy_projectid: '-' - codeclimate: no - codecov: yes - command_line_interface: no + codeclimate: 'no' + codecov: 'yes' + command_line_interface: 'no' command_line_interface_bin_name: '-' - coveralls: yes - coveralls_token: mHSWktPkrw65WSlziG8NnK9m1xj2k1kOQ + coveralls: 'yes' distribution_name: aspectlib email: contact@ionelmc.ro full_name: Ionel Cristian Mărieș - legacy_python: yes + github_actions: 'yes' + github_actions_osx: 'no' + github_actions_windows: 'no' + legacy_python: 'no' license: BSD 2-Clause License linter: flake8 package_name: aspectlib - pre_commit: yes + pre_commit: 'yes' + pre_commit_formatter: black project_name: Aspectlib project_short_description: '``aspectlib`` is an aspect-oriented programming, monkey-patch and decorators library. It is useful when changing' - pypi_badge: yes - pypi_disable_upload: no + pypi_badge: 'yes' + pypi_disable_upload: 'no' release_date: '2020-11-15' repo_hosting: github.com repo_hosting_domain: github.com + repo_main_branch: main repo_name: python-aspectlib repo_username: ionelmc - requiresio: yes - scrutinizer: no - setup_py_uses_setuptools_scm: no - setup_py_uses_test_runner: no - sphinx_docs: yes + requiresio: 'yes' + scrutinizer: 'no' + setup_py_uses_pytest_runner: 'no' + setup_py_uses_setuptools_scm: 'no' + sphinx_docs: 'yes' sphinx_docs_hosting: https://python-aspectlib.readthedocs.io/ - sphinx_doctest: yes + sphinx_doctest: 'yes' sphinx_theme: sphinx-py3doc-enhanced-theme - test_matrix_configurator: yes - test_matrix_separate_coverage: no - test_runner: pytest - travis: yes - travis_osx: no + test_matrix_configurator: 'no' + test_matrix_separate_coverage: 'yes' + travis: 'no' + travis_osx: 'no' version: 1.5.2 + version_manager: bump2version website: http://blog.ionelmc.ro year_from: '2014' - year_to: '2021' + year_to: '2022' diff --git a/.coveragerc b/.coveragerc index 79a44ff..770e36e 100644 --- a/.coveragerc +++ b/.coveragerc @@ -1,12 +1,10 @@ [paths] -source = - src - */site-packages +source = src [run] branch = true source = - aspectlib + src tests parallel = true diff --git a/.github/workflows/github-actions.yml b/.github/workflows/github-actions.yml new file mode 100644 index 0000000..4adf1ad --- /dev/null +++ b/.github/workflows/github-actions.yml @@ -0,0 +1,209 @@ +name: build +on: [push, pull_request] +jobs: + test: + name: ${{ matrix.name }} + runs-on: ${{ matrix.os }} + timeout-minutes: 30 + strategy: + fail-fast: false + matrix: + include: + - name: 'check' + python: '3.9' + toxpython: 'python3.9' + tox_env: 'check' + os: 'ubuntu-latest' + - name: 'docs' + python: '3.9' + toxpython: 'python3.9' + tox_env: 'docs' + os: 'ubuntu-latest' + - name: 'py37-cover-release (ubuntu)' + python: '3.7' + toxpython: 'python3.7' + python_arch: 'x64' + tox_env: 'py37-cover-release,codecov' + os: 'ubuntu-latest' + - name: 'py37-cover-debug (ubuntu)' + python: '3.7' + toxpython: 'python3.7' + python_arch: 'x64' + tox_env: 'py37-cover-debug,codecov' + os: 'ubuntu-latest' + - name: 'py37-nocov-release (ubuntu)' + python: '3.7' + toxpython: 'python3.7' + python_arch: 'x64' + tox_env: 'py37-nocov-release' + os: 'ubuntu-latest' + - name: 'py37-nocov-debug (ubuntu)' + python: '3.7' + toxpython: 'python3.7' + python_arch: 'x64' + tox_env: 'py37-nocov-debug' + os: 'ubuntu-latest' + - name: 'py38-cover-release (ubuntu)' + python: '3.8' + toxpython: 'python3.8' + python_arch: 'x64' + tox_env: 'py38-cover-release,codecov' + os: 'ubuntu-latest' + - name: 'py38-cover-debug (ubuntu)' + python: '3.8' + toxpython: 'python3.8' + python_arch: 'x64' + tox_env: 'py38-cover-debug,codecov' + os: 'ubuntu-latest' + - name: 'py38-nocov-release (ubuntu)' + python: '3.8' + toxpython: 'python3.8' + python_arch: 'x64' + tox_env: 'py38-nocov-release' + os: 'ubuntu-latest' + - name: 'py38-nocov-debug (ubuntu)' + python: '3.8' + toxpython: 'python3.8' + python_arch: 'x64' + tox_env: 'py38-nocov-debug' + os: 'ubuntu-latest' + - name: 'py39-cover-release (ubuntu)' + python: '3.9' + toxpython: 'python3.9' + python_arch: 'x64' + tox_env: 'py39-cover-release,codecov' + os: 'ubuntu-latest' + - name: 'py39-cover-debug (ubuntu)' + python: '3.9' + toxpython: 'python3.9' + python_arch: 'x64' + tox_env: 'py39-cover-debug,codecov' + os: 'ubuntu-latest' + - name: 'py39-nocov-release (ubuntu)' + python: '3.9' + toxpython: 'python3.9' + python_arch: 'x64' + tox_env: 'py39-nocov-release' + os: 'ubuntu-latest' + - name: 'py39-nocov-debug (ubuntu)' + python: '3.9' + toxpython: 'python3.9' + python_arch: 'x64' + tox_env: 'py39-nocov-debug' + os: 'ubuntu-latest' + - name: 'py310-cover-release (ubuntu)' + python: '3.10' + toxpython: 'python3.10' + python_arch: 'x64' + tox_env: 'py310-cover-release,codecov' + os: 'ubuntu-latest' + - name: 'py310-cover-debug (ubuntu)' + python: '3.10' + toxpython: 'python3.10' + python_arch: 'x64' + tox_env: 'py310-cover-debug,codecov' + os: 'ubuntu-latest' + - name: 'py310-nocov-release (ubuntu)' + python: '3.10' + toxpython: 'python3.10' + python_arch: 'x64' + tox_env: 'py310-nocov-release' + os: 'ubuntu-latest' + - name: 'py310-nocov-debug (ubuntu)' + python: '3.10' + toxpython: 'python3.10' + python_arch: 'x64' + tox_env: 'py310-nocov-debug' + os: 'ubuntu-latest' + - name: 'pypy37-cover-release (ubuntu)' + python: 'pypy-3.7' + toxpython: 'pypy3.7' + python_arch: 'x64' + tox_env: 'pypy37-cover-release,codecov' + os: 'ubuntu-latest' + - name: 'pypy37-cover-debug (ubuntu)' + python: 'pypy-3.7' + toxpython: 'pypy3.7' + python_arch: 'x64' + tox_env: 'pypy37-cover-debug,codecov' + os: 'ubuntu-latest' + - name: 'pypy37-nocov-release (ubuntu)' + python: 'pypy-3.7' + toxpython: 'pypy3.7' + python_arch: 'x64' + tox_env: 'pypy37-nocov-release' + os: 'ubuntu-latest' + - name: 'pypy37-nocov-debug (ubuntu)' + python: 'pypy-3.7' + toxpython: 'pypy3.7' + python_arch: 'x64' + tox_env: 'pypy37-nocov-debug' + os: 'ubuntu-latest' + - name: 'pypy38-cover-release (ubuntu)' + python: 'pypy-3.8' + toxpython: 'pypy3.8' + python_arch: 'x64' + tox_env: 'pypy38-cover-release,codecov' + os: 'ubuntu-latest' + - name: 'pypy38-cover-debug (ubuntu)' + python: 'pypy-3.8' + toxpython: 'pypy3.8' + python_arch: 'x64' + tox_env: 'pypy38-cover-debug,codecov' + os: 'ubuntu-latest' + - name: 'pypy38-nocov-release (ubuntu)' + python: 'pypy-3.8' + toxpython: 'pypy3.8' + python_arch: 'x64' + tox_env: 'pypy38-nocov-release' + os: 'ubuntu-latest' + - name: 'pypy38-nocov-debug (ubuntu)' + python: 'pypy-3.8' + toxpython: 'pypy3.8' + python_arch: 'x64' + tox_env: 'pypy38-nocov-debug' + os: 'ubuntu-latest' + - name: 'pypy39-cover-release (ubuntu)' + python: 'pypy-3.9' + toxpython: 'pypy3.9' + python_arch: 'x64' + tox_env: 'pypy39-cover-release,codecov' + os: 'ubuntu-latest' + - name: 'pypy39-cover-debug (ubuntu)' + python: 'pypy-3.9' + toxpython: 'pypy3.9' + python_arch: 'x64' + tox_env: 'pypy39-cover-debug,codecov' + os: 'ubuntu-latest' + - name: 'pypy39-nocov-release (ubuntu)' + python: 'pypy-3.9' + toxpython: 'pypy3.9' + python_arch: 'x64' + tox_env: 'pypy39-nocov-release' + os: 'ubuntu-latest' + - name: 'pypy39-nocov-debug (ubuntu)' + python: 'pypy-3.9' + toxpython: 'pypy3.9' + python_arch: 'x64' + tox_env: 'pypy39-nocov-debug' + os: 'ubuntu-latest' + steps: + - uses: actions/checkout@v2 + with: + fetch-depth: 0 + - uses: actions/setup-python@v2 + with: + python-version: ${{ matrix.python }} + architecture: ${{ matrix.python_arch }} + - name: install dependencies + run: | + python -mpip install --progress-bar=off -r ci/requirements.txt + virtualenv --version + pip --version + tox --version + pip list --format=freeze + - name: test + env: + TOXPYTHON: '${{ matrix.toxpython }}' + run: > + tox -e ${{ matrix.tox_env }} -v diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index 6e974cb..8be60d5 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -5,16 +5,20 @@ exclude: '^(\.tox|ci/templates|\.bumpversion\.cfg)(/|$)' repos: - repo: https://github.com/pre-commit/pre-commit-hooks - rev: master + rev: v4.3.0 hooks: - id: trailing-whitespace - id: end-of-file-fixer - id: debug-statements - repo: https://github.com/timothycrosley/isort - rev: master + rev: 5.10.1 hooks: - id: isort + - repo: https://github.com/psf/black + rev: 22.10.0 + hooks: + - id: black - repo: https://gitlab.com/pycqa/flake8 - rev: master + rev: 3.9.2 hooks: - id: flake8 diff --git a/.travis.yml b/.travis.yml deleted file mode 100644 index 86af895..0000000 --- a/.travis.yml +++ /dev/null @@ -1,124 +0,0 @@ -language: python -dist: xenial -virt: lxd -cache: false -env: - global: - - LD_PRELOAD=/lib/x86_64-linux-gnu/libSegFault.so - - SEGFAULT_SIGNALS=all - - LANG=en_US.UTF-8 -matrix: - include: - - python: '3.6' - env: - - TOXENV=check - - python: '3.6' - env: - - TOXENV=docs - - env: - - TOXENV=py27-cover,codecov,coveralls - python: '2.7' - - env: - - TOXENV=py27-cover-debug,codecov,coveralls - python: '2.7' - - env: - - TOXENV=py27-nocov - python: '2.7' - - env: - - TOXENV=py27-nocov-debug - python: '2.7' - - env: - - TOXENV=py36-cover,codecov,coveralls - python: '3.6' - - env: - - TOXENV=py36-cover-debug,codecov,coveralls - python: '3.6' - - env: - - TOXENV=py36-nocov - python: '3.6' - - env: - - TOXENV=py36-nocov-debug - python: '3.6' - - env: - - TOXENV=py37-cover,codecov,coveralls - python: '3.7' - - env: - - TOXENV=py37-cover-debug,codecov,coveralls - python: '3.7' - - env: - - TOXENV=py37-nocov - python: '3.7' - - env: - - TOXENV=py37-nocov-debug - python: '3.7' - - env: - - TOXENV=py38-cover,codecov,coveralls - python: '3.8' - - env: - - TOXENV=py38-cover-debug,codecov,coveralls - python: '3.8' - - env: - - TOXENV=py38-nocov - python: '3.8' - - env: - - TOXENV=py38-nocov-debug - python: '3.8' - - env: - - TOXENV=py39-cover,codecov,coveralls - python: '3.9' - - env: - - TOXENV=py39-cover-debug,codecov,coveralls - python: '3.9' - - env: - - TOXENV=py39-nocov - python: '3.9' - - env: - - TOXENV=py39-nocov-debug - python: '3.9' - - env: - - TOXENV=pypy-cover,codecov,coveralls - python: 'pypy' - - env: - - TOXENV=pypy-cover-debug,codecov,coveralls - python: 'pypy' - - env: - - TOXENV=pypy-nocov - python: 'pypy' - - env: - - TOXENV=pypy-nocov-debug - python: 'pypy' - - env: - - TOXENV=pypy3-cover,codecov,coveralls - - TOXPYTHON=pypy3 - python: 'pypy3' - - env: - - TOXENV=pypy3-cover-debug,codecov,coveralls - - TOXPYTHON=pypy3 - python: 'pypy3' - - env: - - TOXENV=pypy3-nocov - - TOXPYTHON=pypy3 - python: 'pypy3' - - env: - - TOXENV=pypy3-nocov-debug - - TOXPYTHON=pypy3 - python: 'pypy3' -before_install: - - python --version - - uname -a - - lsb_release -a || true -install: - - python -mpip install --progress-bar=off tox -rci/requirements.txt - - virtualenv --version - - easy_install --version - - pip --version - - tox --version -script: - - tox -v -after_failure: - - cat .tox/log/* - - cat .tox/*/log/* -notifications: - email: - on_success: never - on_failure: always diff --git a/CONTRIBUTING.rst b/CONTRIBUTING.rst index 87647a9..47c71ee 100644 --- a/CONTRIBUTING.rst +++ b/CONTRIBUTING.rst @@ -68,16 +68,12 @@ If you need some code review or feedback while you're developing the code just m For merging, you should: -1. Include passing tests (run ``tox``) [1]_. +1. Include passing tests (run ``tox``). 2. Update documentation when there's new API, functionality etc. 3. Add a note to ``CHANGELOG.rst`` about the changes. 4. Add yourself to ``AUTHORS.rst``. -.. [1] If you don't have all the necessary python versions available locally you can rely on Travis - it will - `run the tests `_ - for each change you add in the pull request. - It will be slower though ... Tips ---- diff --git a/LICENSE b/LICENSE index 176891a..82c46d1 100644 --- a/LICENSE +++ b/LICENSE @@ -1,6 +1,6 @@ BSD 2-Clause License -Copyright (c) 2014-2021, Ionel Cristian Mărieș. All rights reserved. +Copyright (c) 2014-2022, Ionel Cristian Mărieș. All rights reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: diff --git a/MANIFEST.in b/MANIFEST.in index 942b8c3..d0dac9c 100644 --- a/MANIFEST.in +++ b/MANIFEST.in @@ -4,9 +4,14 @@ graft ci graft tests include .bumpversion.cfg -include .coveragerc include .cookiecutterrc +include .coveragerc include .editorconfig +include .github/workflows/github-actions.yml +include .pre-commit-config.yaml +include .readthedocs.yml +include pytest.ini +include tox.ini include AUTHORS.rst include CHANGELOG.rst @@ -14,7 +19,4 @@ include CONTRIBUTING.rst include LICENSE include README.rst -include tox.ini .travis.yml .appveyor.yml .readthedocs.yml .pre-commit-config.yaml -include conftest.py - global-exclude *.py[cod] __pycache__/* *.so *.dylib diff --git a/README.rst b/README.rst index 2a8c89a..d89249c 100644 --- a/README.rst +++ b/README.rst @@ -10,32 +10,28 @@ Overview * - docs - |docs| * - tests - - | |travis| |appveyor| |requires| + - | |github-actions| |requires| | |coveralls| |codecov| * - package - | |version| |wheel| |supported-versions| |supported-implementations| | |commits-since| .. |docs| image:: https://readthedocs.org/projects/python-aspectlib/badge/?style=flat - :target: https://readthedocs.org/projects/python-aspectlib + :target: https://python-aspectlib.readthedocs.io/ :alt: Documentation Status -.. |travis| image:: https://api.travis-ci.com/ionelmc/python-aspectlib.svg?branch=master - :alt: Travis-CI Build Status - :target: https://travis-ci.com/github/ionelmc/python-aspectlib +.. |github-actions| image:: https://github.com/ionelmc/python-aspectlib/actions/workflows/github-actions.yml/badge.svg + :alt: GitHub Actions Build Status + :target: https://github.com/ionelmc/python-aspectlib/actions -.. |appveyor| image:: https://ci.appveyor.com/api/projects/status/github/ionelmc/python-aspectlib?branch=master&svg=true - :alt: AppVeyor Build Status - :target: https://ci.appveyor.com/project/ionelmc/python-aspectlib - -.. |requires| image:: https://requires.io/github/ionelmc/python-aspectlib/requirements.svg?branch=master +.. |requires| image:: https://requires.io/github/ionelmc/python-aspectlib/requirements.svg?branch=main :alt: Requirements Status - :target: https://requires.io/github/ionelmc/python-aspectlib/requirements/?branch=master + :target: https://requires.io/github/ionelmc/python-aspectlib/requirements/?branch=main -.. |coveralls| image:: https://coveralls.io/repos/ionelmc/python-aspectlib/badge.svg?branch=master&service=github +.. |coveralls| image:: https://coveralls.io/repos/ionelmc/python-aspectlib/badge.svg?branch=main&service=github :alt: Coverage Status :target: https://coveralls.io/r/ionelmc/python-aspectlib -.. |codecov| image:: https://codecov.io/gh/ionelmc/python-aspectlib/branch/master/graphs/badge.svg?branch=master +.. |codecov| image:: https://codecov.io/gh/ionelmc/python-aspectlib/branch/main/graphs/badge.svg?branch=main :alt: Coverage Status :target: https://codecov.io/github/ionelmc/python-aspectlib @@ -57,7 +53,7 @@ Overview .. |commits-since| image:: https://img.shields.io/github/commits-since/ionelmc/python-aspectlib/v1.5.2.svg :alt: Commits since latest release - :target: https://github.com/ionelmc/python-aspectlib/compare/v1.5.2...master + :target: https://github.com/ionelmc/python-aspectlib/compare/v1.5.2...main @@ -76,6 +72,11 @@ Installation pip install aspectlib +You can also install the in-development version with:: + + pip install https://github.com/ionelmc/python-aspectlib/archive/main.zip + + Documentation ============= diff --git a/ci/appveyor-with-compiler.cmd b/ci/appveyor-with-compiler.cmd deleted file mode 100644 index 289585f..0000000 --- a/ci/appveyor-with-compiler.cmd +++ /dev/null @@ -1,23 +0,0 @@ -:: Very simple setup: -:: - if WINDOWS_SDK_VERSION is set then activate the SDK. -:: - disable the WDK if it's around. - -SET COMMAND_TO_RUN=%* -SET WIN_SDK_ROOT=C:\Program Files\Microsoft SDKs\Windows -SET WIN_WDK="c:\Program Files (x86)\Windows Kits\10\Include\wdf" -ECHO SDK: %WINDOWS_SDK_VERSION% ARCH: %PYTHON_ARCH% - -IF EXIST %WIN_WDK% ( - REM See: https://connect.microsoft.com/VisualStudio/feedback/details/1610302/ - REN %WIN_WDK% 0wdf -) -IF "%WINDOWS_SDK_VERSION%"=="" GOTO main - -SET DISTUTILS_USE_SDK=1 -SET MSSdk=1 -"%WIN_SDK_ROOT%\%WINDOWS_SDK_VERSION%\Setup\WindowsSdkVer.exe" -q -version:%WINDOWS_SDK_VERSION% -CALL "%WIN_SDK_ROOT%\%WINDOWS_SDK_VERSION%\Bin\SetEnv.cmd" /x64 /release - -:main -ECHO Executing: %COMMAND_TO_RUN% -CALL %COMMAND_TO_RUN% || EXIT 1 diff --git a/ci/bootstrap.py b/ci/bootstrap.py index 7e42276..cc5ce2f 100755 --- a/ci/bootstrap.py +++ b/ci/bootstrap.py @@ -1,8 +1,6 @@ #!/usr/bin/env python # -*- coding: utf-8 -*- -from __future__ import absolute_import -from __future__ import print_function -from __future__ import unicode_literals + import os import subprocess @@ -11,8 +9,10 @@ from os.path import dirname from os.path import exists from os.path import join +from os.path import relpath base_path = dirname(dirname(abspath(__file__))) +templates_path = join(base_path, "ci", "templates") def check_call(args): @@ -38,7 +38,7 @@ def exec_in_env(): except subprocess.CalledProcessError: check_call(["virtualenv", env_path]) print("Installing `jinja2` into bootstrap environment...") - check_call([join(bin_path, "pip"), "install", "jinja2", "tox", "matrix"]) + check_call([join(bin_path, "pip"), "install", "jinja2", "tox"]) python_executable = join(bin_path, "python") if not os.path.exists(python_executable): python_executable += '.exe' @@ -50,34 +50,33 @@ def exec_in_env(): def main(): import jinja2 - import matrix print("Project path: {0}".format(base_path)) jinja = jinja2.Environment( - loader=jinja2.FileSystemLoader(join(base_path, "ci", "templates")), + loader=jinja2.FileSystemLoader(templates_path), trim_blocks=True, lstrip_blocks=True, - keep_trailing_newline=True + keep_trailing_newline=True, ) - tox_environments = {} - for (alias, conf) in matrix.from_file(join(base_path, "setup.cfg")).items(): - deps = conf["dependencies"] - tox_environments[alias] = { - "deps": deps.split(), - } - if "coverage_flags" in conf: - cover = {"false": False, "true": True}[conf["coverage_flags"].lower()] - tox_environments[alias].update(cover=cover) - if "environment_variables" in conf: - env_vars = conf["environment_variables"] - tox_environments[alias].update(env_vars=env_vars.split()) - - for name in os.listdir(join("ci", "templates")): - with open(join(base_path, name), "w") as fh: - fh.write(jinja.get_template(name).render(tox_environments=tox_environments)) - print("Wrote {}".format(name)) + tox_environments = [ + line.strip() + # 'tox' need not be installed globally, but must be importable + # by the Python that is running this script. + # This uses sys.executable the same way that the call in + # cookiecutter-pylibrary/hooks/post_gen_project.py + # invokes this bootstrap.py itself. + for line in subprocess.check_output([sys.executable, '-m', 'tox', '--listenvs'], universal_newlines=True).splitlines() + ] + tox_environments = [line for line in tox_environments if line.startswith('py')] + + for root, _, files in os.walk(templates_path): + for name in files: + relative = relpath(root, templates_path) + with open(join(base_path, relative, name), "w") as fh: + fh.write(jinja.get_template(join(relative, name)).render(tox_environments=tox_environments)) + print("Wrote {}".format(name)) print("DONE.") diff --git a/ci/requirements.txt b/ci/requirements.txt index d7f5177..a0ef106 100644 --- a/ci/requirements.txt +++ b/ci/requirements.txt @@ -2,3 +2,4 @@ virtualenv>=16.6.0 pip>=19.1.1 setuptools>=18.0.1 six>=1.14.0 +tox diff --git a/ci/templates/.appveyor.yml b/ci/templates/.appveyor.yml deleted file mode 100644 index 00704b9..0000000 --- a/ci/templates/.appveyor.yml +++ /dev/null @@ -1,53 +0,0 @@ -version: '{branch}-{build}' -build: off -image: Visual Studio 2019 -environment: - global: - COVERALLS_EXTRAS: '-v' - COVERALLS_REPO_TOKEN: mHSWktPkrw65WSlziG8NnK9m1xj2k1kOQ - matrix: - - TOXENV: check - TOXPYTHON: C:\Python36\python.exe - PYTHON_HOME: C:\Python36 - PYTHON_VERSION: '3.6' - PYTHON_ARCH: '32' -{% for env, config in tox_environments|dictsort %} -{% if env.startswith(('py2', 'py3')) %} - - TOXENV: {{ env }},codecov,coveralls{{ "" }} - TOXPYTHON: C:\Python{{ env[2:4] }}\python.exe - PYTHON_HOME: C:\Python{{ env[2:4] }} - PYTHON_VERSION: '{{ env[2] }}.{{ env[3] }}' - PYTHON_ARCH: '32' -{% if 'nocov' in env %} - WHEEL_PATH: .tox/dist -{% endif %} - - TOXENV: {{ env }},codecov,coveralls{{ "" }} - TOXPYTHON: C:\Python{{ env[2:4] }}-x64\python.exe - PYTHON_HOME: C:\Python{{ env[2:4] }}-x64 - PYTHON_VERSION: '{{ env[2] }}.{{ env[3] }}' - PYTHON_ARCH: '64' -{% if 'nocov' in env %} - WHEEL_PATH: .tox/dist -{% endif %} -{% if env.startswith('py2') %} - WINDOWS_SDK_VERSION: v7.0 -{% endif %} -{% endif %}{% endfor %} -init: - - ps: echo $env:TOXENV - - ps: ls C:\Python* -install: - - '%PYTHON_HOME%\python -mpip install --progress-bar=off tox -rci/requirements.txt' - - '%PYTHON_HOME%\Scripts\virtualenv --version' - - '%PYTHON_HOME%\Scripts\easy_install --version' - - '%PYTHON_HOME%\Scripts\pip --version' - - '%PYTHON_HOME%\Scripts\tox --version' -test_script: - - cmd /E:ON /V:ON /C .\ci\appveyor-with-compiler.cmd %PYTHON_HOME%\Scripts\tox -on_failure: - - ps: dir "env:" - - ps: get-content .tox\*\log\* - -### To enable remote debugging uncomment this (also, see: http://www.appveyor.com/docs/how-to/rdp-to-build-worker): -# on_finish: -# - ps: $blockRdp = $true; iex ((new-object net.webclient).DownloadString('https://raw.githubusercontent.com/appveyor/ci/master/scripts/enable-rdp.ps1')) diff --git a/ci/templates/.github/workflows/github-actions.yml b/ci/templates/.github/workflows/github-actions.yml new file mode 100644 index 0000000..b4a7ae2 --- /dev/null +++ b/ci/templates/.github/workflows/github-actions.yml @@ -0,0 +1,63 @@ +name: build +on: [push, pull_request] +jobs: + test: + name: {{ '${{ matrix.name }}' }} + runs-on: {{ '${{ matrix.os }}' }} + timeout-minutes: 30 + strategy: + fail-fast: false + matrix: + include: + - name: 'check' + python: '3.9' + toxpython: 'python3.9' + tox_env: 'check' + os: 'ubuntu-latest' + - name: 'docs' + python: '3.9' + toxpython: 'python3.9' + tox_env: 'docs' + os: 'ubuntu-latest' +{% for env in tox_environments %} +{% set prefix = env.split('-')[0] -%} +{% if prefix.startswith('pypy') %} +{% set python %}pypy-{{ prefix[4] }}.{{ prefix[5] }}{% endset %} +{% set cpython %}pp{{ prefix[4:5] }}{% endset %} +{% set toxpython %}pypy{{ prefix[4] }}.{{ prefix[5] }}{% endset %} +{% else %} +{% set python %}{{ prefix[2] }}.{{ prefix[3:] }}{% endset %} +{% set cpython %}cp{{ prefix[2:] }}{% endset %} +{% set toxpython %}python{{ prefix[2] }}.{{ prefix[3:] }}{% endset %} +{% endif %} +{% for os, python_arch in [ + ['ubuntu', 'x64'], +] %} + - name: '{{ env }} ({{ os }})' + python: '{{ python }}' + toxpython: '{{ toxpython }}' + python_arch: '{{ python_arch }}' + tox_env: '{{ env }}{% if 'cover' in env %},codecov{% endif %}' + os: '{{ os }}-latest' +{% endfor %} +{% endfor %} + steps: + - uses: actions/checkout@v2 + with: + fetch-depth: 0 + - uses: actions/setup-python@v2 + with: + python-version: {{ '${{ matrix.python }}' }} + architecture: {{ '${{ matrix.python_arch }}' }} + - name: install dependencies + run: | + python -mpip install --progress-bar=off -r ci/requirements.txt + virtualenv --version + pip --version + tox --version + pip list --format=freeze + - name: test + env: + TOXPYTHON: '{{ '${{ matrix.toxpython }}' }}' + run: > + tox -e {{ '${{ matrix.tox_env }}' }} -v diff --git a/ci/templates/.travis.yml b/ci/templates/.travis.yml deleted file mode 100644 index 2b41139..0000000 --- a/ci/templates/.travis.yml +++ /dev/null @@ -1,48 +0,0 @@ -language: python -dist: xenial -virt: lxd -cache: false -env: - global: - - LD_PRELOAD=/lib/x86_64-linux-gnu/libSegFault.so - - SEGFAULT_SIGNALS=all - - LANG=en_US.UTF-8 -matrix: - include: - - python: '3.6' - env: - - TOXENV=check - - python: '3.6' - env: - - TOXENV=docs -{%- for env, config in tox_environments|dictsort %}{{ '' }} - - env: - - TOXENV={{ env }}{% if config.cover %},codecov,coveralls{% endif %} -{%- if env.startswith('pypy3') %}{{ '' }} - - TOXPYTHON=pypy3 - python: 'pypy3' -{%- elif env.startswith('pypy') %}{{ '' }} - python: 'pypy' -{%- else %}{{ '' }} - python: '{{ '{0[2]}.{0[3]}'.format(env) }}' -{%- endif %}{{ '' }} -{%- endfor %}{{ '' }} -before_install: - - python --version - - uname -a - - lsb_release -a || true -install: - - python -mpip install --progress-bar=off tox -rci/requirements.txt - - virtualenv --version - - easy_install --version - - pip --version - - tox --version -script: - - tox -v -after_failure: - - cat .tox/log/* - - cat .tox/*/log/* -notifications: - email: - on_success: never - on_failure: always diff --git a/ci/templates/tox.ini b/ci/templates/tox.ini deleted file mode 100644 index eaef583..0000000 --- a/ci/templates/tox.ini +++ /dev/null @@ -1,119 +0,0 @@ -[tox] -envlist = - clean, - check, - docs, -{% for env in tox_environments|sort %} - {{ env }}, -{% endfor %} - report - -[testenv] -basepython = - {bootstrap,clean,check,report,docs,codecov,coveralls}: {env:TOXPYTHON:python3} -setenv = - PYTHONPATH={toxinidir}/tests - PYTHONUNBUFFERED=yes -passenv = - * -deps = - hunter - mock - nose - process-tests - pytest - pytest-catchlog - pytest-clarity - pytest-cov - pytest-travis-fold - tornado<6.0 -commands = - {posargs:pytest --cov --cov-report=term-missing -vv --ignore=src} - -[testenv:bootstrap] -deps = - jinja2 - matrix -skip_install = true -commands = - python ci/bootstrap.py --no-env - -[testenv:check] -deps = - docutils - check-manifest - flake8 - readme-renderer - pygments - isort -skip_install = true -commands = - python setup.py check --strict --metadata --restructuredtext - check-manifest {toxinidir} - flake8 - isort --verbose --check-only --diff --filter-files . - - -[testenv:docs] -usedevelop = true -deps = - -r{toxinidir}/docs/requirements.txt -commands = - sphinx-build {posargs:-E} -b doctest docs dist/docs - sphinx-build {posargs:-E} -b html docs dist/docs - sphinx-build -b linkcheck docs dist/docs - -[testenv:coveralls] -deps = - coveralls -skip_install = true -commands = - coveralls [] - - - -[testenv:codecov] -deps = - codecov -skip_install = true -commands = - codecov [] - -[testenv:report] -deps = coverage -skip_install = true -commands = - coverage report - coverage html - -[testenv:clean] -commands = coverage erase -skip_install = true -deps = coverage -{% for env, config in tox_environments|dictsort %} - -[testenv:{{ env }}] -basepython = {env:TOXPYTHON:{{ env.split("-")[0] if env.startswith("pypy") else "python{0[2]}.{0[3]}".format(env) }}} -{% if config.cover or config.env_vars %} -setenv = - {[testenv]setenv} -{% endif %} -{% for var in config.env_vars %} - {{ var }} -{% endfor %} -{% if config.cover %} -usedevelop = true -commands = - {posargs:pytest --cov --cov-report=term-missing -vv} -{% endif %} -{% if config.cover or config.deps %} -deps = - {[testenv]deps} -{% endif %} -{% if config.cover %} - pytest-cov -{% endif %} -{% for dep in config.deps %} - {{ dep }} -{% endfor -%} -{% endfor -%} diff --git a/conftest.py b/conftest.py index e237da2..ee54d9d 100644 --- a/conftest.py +++ b/conftest.py @@ -1,10 +1,5 @@ -import sys - -PY3 = sys.version_info[0] >= 3 - - def pytest_ignore_collect(path, config): basename = path.basename - if not PY3 and "py3" in basename or PY3 and "py2" in basename or 'pytest' in basename: + if 'pytestsupport' in basename: return True diff --git a/docs/conf.py b/docs/conf.py index a0b738d..cd0320c 100644 --- a/docs/conf.py +++ b/docs/conf.py @@ -1,5 +1,5 @@ # -*- coding: utf-8 -*- -from __future__ import unicode_literals + import sphinx_py3doc_enhanced_theme @@ -17,7 +17,7 @@ source_suffix = '.rst' master_doc = 'index' project = 'Aspectlib' -year = '2014-2021' +year = '2014-2022' author = 'Ionel Cristian Mărieș' copyright = '{0}, {1}'.format(year, author) version = release = '1.5.2' @@ -28,17 +28,17 @@ 'issue': ('https://github.com/ionelmc/python-aspectlib/issues/%s', '#'), 'pr': ('https://github.com/ionelmc/python-aspectlib/pull/%s', 'PR #'), } -html_theme = "sphinx_py3doc_enhanced_theme" +html_theme = 'sphinx_py3doc_enhanced_theme' html_theme_path = [sphinx_py3doc_enhanced_theme.get_html_theme_path()] html_theme_options = { - 'githuburl': 'https://github.com/ionelmc/python-aspectlib/' + 'githuburl': 'https://github.com/ionelmc/python-aspectlib/', } html_use_smartypants = True html_last_updated_fmt = '%b %d, %Y' html_split_index = False html_sidebars = { - '**': ['searchbox.html', 'globaltoc.html', 'sourcelink.html'], + '**': ['searchbox.html', 'globaltoc.html', 'sourcelink.html'], } html_short_title = '%s-%s' % (project, version) diff --git a/docs/spelling_wordlist.txt b/docs/spelling_wordlist.txt index 89586e1..d3b0b2f 100644 --- a/docs/spelling_wordlist.txt +++ b/docs/spelling_wordlist.txt @@ -1,5 +1,8 @@ aspectlib builtin +builtins +classmethod +staticmethod classmethods staticmethods mumbo diff --git a/pyproject.toml b/pyproject.toml new file mode 100644 index 0000000..fc9d53d --- /dev/null +++ b/pyproject.toml @@ -0,0 +1,10 @@ +[build-system] +requires = [ + "setuptools>=30.3.0", + "wheel", +] + +[tool.black] +line-length = 140 +target-version = ['py37'] +skip-string-normalization = true diff --git a/pytest.ini b/pytest.ini new file mode 100644 index 0000000..de53382 --- /dev/null +++ b/pytest.ini @@ -0,0 +1,38 @@ +[pytest] +# If a pytest section is found in one of the possible config files +# (pytest.ini, tox.ini or setup.cfg), then pytest will not look for any others, +# so if you add a pytest config section elsewhere, +# you will need to delete this section from setup.cfg. +norecursedirs = + .git + .tox + .env + dist + build + migrations + +python_files = + test_*.py + *_test.py + tests.py +addopts = + -ra + --strict-markers + --ignore=docs/conf.py + --ignore=setup.py + --ignore=ci + --ignore=.eggs + --doctest-modules + --doctest-glob=\*.rst + --tb=short +testpaths = + tests + +# Idea from: https://til.simonwillison.net/pytest/treat-warnings-as-errors +filterwarnings = + error + ignore:Setting test_aspectlib.MissingGlobal to . There was no previous definition, probably patching the wrong module. +# You can add exclusions, some examples: +# ignore:'aspectlib' defines default_app_config:PendingDeprecationWarning:: +# ignore:The {{% if::: +# ignore:Coverage disabled via --no-cov switch! diff --git a/setup.cfg b/setup.cfg index a4f0d99..e617fb4 100644 --- a/setup.cfg +++ b/setup.cfg @@ -1,35 +1,7 @@ -[bdist_wheel] -universal = 1 - [flake8] max-line-length = 140 exclude = .tox,.eggs,ci/templates,build,dist -[tool:pytest] -# If a pytest section is found in one of the possible config files -# (pytest.ini, tox.ini or setup.cfg), then pytest will not look for any others, -# so if you add a pytest config section elsewhere, -# you will need to delete this section from setup.cfg. -norecursedirs = - migrations - -python_files = - test_*.py - *_test.py - tests.py -addopts = - -ra - --strict - --ignore=docs/conf.py - --ignore=setup.py - --ignore=ci - --ignore=.eggs - --doctest-modules - --doctest-glob=\*.rst - --tb=short -testpaths = - tests - [tool:isort] force_single_line = True line_length = 120 @@ -37,45 +9,3 @@ known_first_party = aspectlib default_section = THIRDPARTY forced_separate = test_aspectlib skip = .tox,.eggs,ci/templates,build,dist - -[matrix] -# This is the configuration for the `./bootstrap.py` script. -# It generates `.travis.yml`, `tox.ini` and `.appveyor.yml`. -# -# Syntax: [alias:] value [!variable[glob]] [&variable[glob]] -# -# alias: -# - is used to generate the tox environment -# - it's optional -# - if not present the alias will be computed from the `value` -# value: -# - a value of "-" means empty -# !variable[glob]: -# - exclude the combination of the current `value` with -# any value matching the `glob` in `variable` -# - can use as many you want -# &variable[glob]: -# - only include the combination of the current `value` -# when there's a value matching `glob` in `variable` -# - can use as many you want - -python_versions = - py27 - py36 - py37 - py38 - py39 - pypy - pypy3 - -dependencies = - : trollius !python_versions[py3*] !python_versions[pypy3] - : !python_versions[py2*] !python_versions[pypy] - -coverage_flags = - cover: true - nocov: false - -environment_variables = - debug: ASPECTLIB_DEBUG=yes - - diff --git a/setup.py b/setup.py index a9f305e..053f6b6 100755 --- a/setup.py +++ b/setup.py @@ -1,7 +1,5 @@ #!/usr/bin/env python # -*- encoding: utf-8 -*- -from __future__ import absolute_import -from __future__ import print_function import io import re @@ -16,10 +14,7 @@ def read(*names, **kwargs): - with io.open( - join(dirname(__file__), *names), - encoding=kwargs.get('encoding', 'utf8') - ) as fh: + with io.open(join(dirname(__file__), *names), encoding=kwargs.get('encoding', 'utf8')) as fh: return fh.read() @@ -28,9 +23,9 @@ def read(*names, **kwargs): version='1.5.2', license='BSD-2-Clause', description='``aspectlib`` is an aspect-oriented programming, monkey-patch and decorators library. It is useful when changing', - long_description='%s\n%s' % ( + long_description='{}\n{}'.format( re.compile('^.. start-badges.*^.. end-badges', re.M | re.S).sub('', read('README.rst')), - re.sub(':[a-z]+:`~?(.*?)`', r'``\1``', read('CHANGELOG.rst')) + re.sub(':[a-z]+:`~?(.*?)`', r'``\1``', read('CHANGELOG.rst')), ), author='Ionel Cristian Mărieș', author_email='contact@ionelmc.ro', @@ -49,12 +44,12 @@ def read(*names, **kwargs): 'Operating System :: POSIX', 'Operating System :: Microsoft :: Windows', 'Programming Language :: Python', - 'Programming Language :: Python :: 2.7', 'Programming Language :: Python :: 3', - 'Programming Language :: Python :: 3.6', + 'Programming Language :: Python :: 3 :: Only', 'Programming Language :: Python :: 3.7', 'Programming Language :: Python :: 3.8', 'Programming Language :: Python :: 3.9', + 'Programming Language :: Python :: 3.10', 'Programming Language :: Python :: Implementation :: CPython', 'Programming Language :: Python :: Implementation :: PyPy', # uncomment if you test on these interpreters: @@ -69,19 +64,32 @@ def read(*names, **kwargs): 'Issue Tracker': 'https://github.com/ionelmc/python-aspectlib/issues', }, keywords=[ - 'aop', 'aspects', 'aspect oriented programming', 'decorators', 'patch', 'monkeypatch', 'weave', 'debug', 'log', - 'tests', 'mock', 'capture', 'replay', 'capture-replay', 'debugging', 'patching', 'monkeypatching', 'record', - 'recording', 'mocking', 'logger' + 'aop', + 'aspects', + 'aspect oriented programming', + 'decorators', + 'patch', + 'monkeypatch', + 'weave', + 'debug', + 'log', + 'tests', + 'mock', + 'capture', + 'replay', + 'capture-replay', + 'debugging', + 'patching', + 'monkeypatching', + 'record', + 'recording', + 'mocking', + 'logger', ], python_requires='>=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*, !=3.5.*', - install_requires=[ - 'fields' - ], - extras_require={ - }, + install_requires=['fields'], + extras_require={}, entry_points={ - 'pytest11': [ - 'aspectlib = aspectlib.pytestsupport' - ], - } + 'pytest11': ['aspectlib = aspectlib.pytestsupport'], + }, ) diff --git a/src/aspectlib/__init__.py b/src/aspectlib/__init__.py index b85f1dd..a62ef18 100644 --- a/src/aspectlib/__init__.py +++ b/src/aspectlib/__init__.py @@ -1,5 +1,4 @@ -from __future__ import print_function - +import builtins import re import sys import warnings @@ -15,9 +14,7 @@ from inspect import isroutine from logging import getLogger -from .utils import PY2 from .utils import PY3 -from .utils import PY37plus from .utils import Sentinel from .utils import basestring from .utils import force_bind @@ -30,10 +27,6 @@ except ImportError: InstanceType = None -try: - import __builtin__ -except ImportError: - import builtins as __builtin__ # pylint: disable=F0401 try: from types import ClassType @@ -53,6 +46,7 @@ def isasyncfunction(obj): return iscoroutinefunction(obj) else: return isasyncgenfunction(obj) or iscoroutinefunction(obj) + except ImportError: isasyncfunction = None @@ -97,6 +91,7 @@ class Proceed(object): If not used as an instance then the default args and kwargs are used. """ + __slots__ = 'args', 'kwargs' def __init__(self, *args, **kwargs): @@ -110,7 +105,8 @@ class Return(object): If not used as an instance then ``None`` is returned. """ - __slots__ = 'value', + + __slots__ = ('value',) def __init__(self, value): self.value = value @@ -158,6 +154,7 @@ class Aspect(object): ... and the result is: None """ + __slots__ = 'advising_function', 'bind' def __new__(cls, advising_function=UNSPECIFIED, bind=False): @@ -177,98 +174,14 @@ def __init__(self, advising_function, bind=False): def __call__(self, cutpoint_function): if isasyncfunction is not None and isasyncfunction(cutpoint_function): from aspectlib.py35support import decorate_advising_asyncgenerator_py35 + return decorate_advising_asyncgenerator_py35(self.advising_function, cutpoint_function, self.bind) elif isgeneratorfunction(cutpoint_function): - if PY37plus: - from aspectlib.py35support import decorate_advising_generator_py35 - return decorate_advising_generator_py35(self.advising_function, cutpoint_function, self.bind) - elif PY3: - from aspectlib.py3support import decorate_advising_generator_py3 - return decorate_advising_generator_py3(self.advising_function, cutpoint_function, self.bind) - else: - def advising_generator_wrapper(*args, **kwargs): - if self.bind: - advisor = self.advising_function(cutpoint_function, *args, **kwargs) - else: - advisor = self.advising_function(*args, **kwargs) - if not isgenerator(advisor): - raise ExpectedGenerator("advising_function %s did not return a generator." % self.advising_function) - try: - advice = next(advisor) - while True: - logdebug('Got advice %r from %s', advice, self.advising_function) - if advice is Proceed or advice is None or isinstance(advice, Proceed): - if isinstance(advice, Proceed): - args = advice.args - kwargs = advice.kwargs - gen = cutpoint_function(*args, **kwargs) - try: - try: - generated = next(gen) - except StopIteration as exc: - logexception("The cutpoint has been exhausted (early).") - result = exc.args - if result: - if len(result) == 1: - result = exc.args[0] - else: - result = None - else: - while True: - try: - sent = yield generated - except GeneratorExit as exc: - logexception("Got GeneratorExit while consuming the cutpoint") - gen.close() - raise exc - except BaseException as exc: - logexception("Got exception %r. Throwing it the cutpoint", exc) - try: - generated = gen.throw(*sys.exc_info()) - except StopIteration as exc: - logexception("The cutpoint has been exhausted.") - result = exc.args - if result: - if len(result) == 1: - result = exc.args[0] - else: - result = None - break - else: - try: - if sent is None: - generated = next(gen) - else: - generated = gen.send(sent) - except StopIteration as exc: - logexception("The cutpoint has been exhausted.") - result = exc.args - if result: - if len(result) == 1: - result = exc.args[0] - else: - result = None - break - except BaseException as exc: # noqa - advice = advisor.throw(*sys.exc_info()) - else: - try: - advice = advisor.send(result) - except StopIteration: - raise StopIteration(result) - finally: - gen.close() - elif advice is Return: - return - elif isinstance(advice, Return): - raise StopIteration(advice.value) - else: - raise UnacceptableAdvice("Unknown advice %s" % advice) - finally: - advisor.close() + from aspectlib.py35support import decorate_advising_generator_py35 - return mimic(advising_generator_wrapper, cutpoint_function) + return decorate_advising_generator_py35(self.advising_function, cutpoint_function, self.bind) else: + def advising_function_wrapper(*args, **kwargs): if self.bind: advisor = self.advising_function(cutpoint_function, *args, **kwargs) @@ -313,6 +226,7 @@ class Rollback(object): """ When called, rollbacks all the patches and changes the :func:`weave` has done. """ + __slots__ = '_rollbacks' def __init__(self, rollback=None): @@ -383,9 +297,7 @@ def _check_name(name): if not VALID_IDENTIFIER.match(name): raise SyntaxError( "Could not match %r to %r. It should be a string of " - "letters, numbers and underscore that starts with a letter or underscore." % ( - name, VALID_IDENTIFIER.pattern - ) + "letters, numbers and underscore that starts with a letter or underscore." % (name, VALID_IDENTIFIER.pattern) ) @@ -434,9 +346,7 @@ def weave(target, aspects, **options): bag = options.setdefault('bag', ObjectBag()) if isinstance(target, (list, tuple)): - return Rollback([ - weave(item, aspects, **options) for item in target - ]) + return Rollback([weave(item, aspects, **options) for item in target]) elif isinstance(target, basestring): parts = target.split('.') for part in parts: @@ -467,10 +377,7 @@ def weave(target, aspects, **options): if isinstance(obj, (type, ClassType)): logdebug(" .. as a class %r.", obj) - return weave_class( - obj, aspects, - owner=owner, name=name, **options - ) + return weave_class(obj, aspects, owner=owner, name=name, **options) elif callable(obj): # or isinstance(obj, FunctionType) ?? logdebug(" .. as a callable %r.", obj) if bag.has(obj): @@ -480,10 +387,10 @@ def weave(target, aspects, **options): return weave(obj, aspects, **options) name = getattr(target, '__name__', None) - if name and getattr(__builtin__, name, None) is target: + if name and getattr(builtins, name, None) is target: if bag.has(target): return Nothing - return patch_module_function(__builtin__, target, aspects, **options) + return patch_module_function(builtins, target, aspects, **options) elif PY3 and ismethod(target): if bag.has(target): return Nothing @@ -504,24 +411,6 @@ def weave(target, aspects, **options): logdebug("@ patching %r (%s) as a property.", target, name) func = owner.__dict__[name] return patch_module(owner, name, _checked_apply(aspects, func), func, **options) - elif PY2 and isfunction(target): - if bag.has(target): - return Nothing - return patch_module_function(_import_module(target.__module__), target, aspects, **options) - elif PY2 and ismethod(target): - if target.im_self: - if bag.has(target): - return Nothing - inst = target.im_self - name = target.__name__ - logdebug("@ patching %r (%s) as instance method.", target, name) - func = target.im_func - setattr(inst, name, _checked_apply(aspects, func).__get__(inst, type(inst))) - return Rollback(lambda: delattr(inst, name)) - else: - klass = target.im_class - name = target.__name__ - return weave(klass, aspects, methods='%s$' % name, **options) elif isclass(target): return weave_class(target, aspects, **options) elif ismodule(target): @@ -560,8 +449,7 @@ def weave_instance(instance, aspect, methods=NORMAL_METHODS, lazy=False, bag=Bro entanglement = Rollback() method_matches = make_method_matcher(methods) - logdebug("weave_instance (module=%r, aspect=%s, methods=%s, lazy=%s, **options=%s)", - instance, aspect, methods, lazy, options) + logdebug("weave_instance (module=%r, aspect=%s, methods=%s, lazy=%s, **options=%s)", instance, aspect, methods, lazy, options) def fixup(func): return func.__get__(instance, type(instance)) @@ -576,9 +464,7 @@ def fixup(func): realfunc = func.__func__ else: realfunc = func.im_func - entanglement.merge( - patch_module(instance, attr, _checked_apply(fixed_aspect, realfunc, module=None), **options) - ) + entanglement.merge(patch_module(instance, attr, _checked_apply(fixed_aspect, realfunc, module=None), **options)) return entanglement @@ -595,8 +481,7 @@ def weave_module(module, aspect, methods=NORMAL_METHODS, lazy=False, bag=BrokenB entanglement = Rollback() method_matches = make_method_matcher(methods) - logdebug("weave_module (module=%r, aspect=%s, methods=%s, lazy=%s, **options=%s)", - module, aspect, methods, lazy, options) + logdebug("weave_module (module=%r, aspect=%s, methods=%s, lazy=%s, **options=%s)", module, aspect, methods, lazy, options) for attr in dir(module): if method_matches(attr): @@ -613,8 +498,9 @@ def weave_module(module, aspect, methods=NORMAL_METHODS, lazy=False, bag=BrokenB return entanglement -def weave_class(klass, aspect, methods=NORMAL_METHODS, subclasses=True, lazy=False, - owner=None, name=None, aliases=True, bases=True, bag=BrokenBag): +def weave_class( + klass, aspect, methods=NORMAL_METHODS, subclasses=True, lazy=False, owner=None, name=None, aliases=True, bases=True, bag=BrokenBag +): """ Low-level weaver for classes. @@ -627,8 +513,17 @@ def weave_class(klass, aspect, methods=NORMAL_METHODS, subclasses=True, lazy=Fal entanglement = Rollback() method_matches = make_method_matcher(methods) - logdebug("weave_class (klass=%r, methods=%s, subclasses=%s, lazy=%s, owner=%s, name=%s, aliases=%s, bases=%s)", - klass, methods, subclasses, lazy, owner, name, aliases, bases) + logdebug( + "weave_class (klass=%r, methods=%s, subclasses=%s, lazy=%s, owner=%s, name=%s, aliases=%s, bases=%s)", + klass, + methods, + subclasses, + lazy, + owner, + name, + aliases, + bases, + ) if subclasses and hasattr(klass, '__subclasses__'): sub_targets = klass.__subclasses__() @@ -636,9 +531,9 @@ def weave_class(klass, aspect, methods=NORMAL_METHODS, subclasses=True, lazy=Fal logdebug("~ weaving subclasses: %s", sub_targets) for sub_class in sub_targets: if not issubclass(sub_class, Fabric): - entanglement.merge(weave_class(sub_class, aspect, - methods=methods, subclasses=subclasses, lazy=lazy, bag=bag)) + entanglement.merge(weave_class(sub_class, aspect, methods=methods, subclasses=subclasses, lazy=lazy, bag=bag)) if lazy: + def __init__(self, *args, **kwargs): super(SubClass, self).__init__(*args, **kwargs) for attr in dir(self): @@ -647,9 +542,7 @@ def __init__(self, *args, **kwargs): if isroutine(func): setattr(self, attr, _checked_apply(aspect, force_bind(func)).__get__(self, SubClass)) - wrappers = { - '__init__': _checked_apply(aspect, __init__) if method_matches('__init__') else __init__ - } + wrappers = {'__init__': _checked_apply(aspect, __init__) if method_matches('__init__') else __init__} for attr, func in klass.__dict__.items(): if method_matches(attr): if ismethoddescriptor(func): @@ -671,9 +564,7 @@ def __init__(self, *args, **kwargs): else: continue original[attr] = func - entanglement.merge(lambda: deque(( - setattr(klass, attr, func) for attr, func in original.items() - ), maxlen=0)) + entanglement.merge(lambda: deque((setattr(klass, attr, func) for attr, func in original.items()), maxlen=0)) if bases: super_original = set() for sklass in _find_super_classes(klass): @@ -681,15 +572,12 @@ def __init__(self, *args, **kwargs): for attr, func in sklass.__dict__.items(): if method_matches(attr) and attr not in original and attr not in super_original: if isroutine(func): - logdebug("@ patching attribute %r (from superclass: %s, original: %r).", - attr, sklass.__name__, func) + logdebug("@ patching attribute %r (from superclass: %s, original: %r).", attr, sklass.__name__, func) setattr(klass, attr, _rewrap_method(func, sklass, aspect)) else: continue super_original.add(attr) - entanglement.merge(lambda: deque(( - delattr(klass, attr) for attr in super_original - ), maxlen=0)) + entanglement.merge(lambda: deque((delattr(klass, attr) for attr in super_original), maxlen=0)) return entanglement @@ -753,9 +641,9 @@ def patch_module(module, name, replacement, original=UNSPECIFIED, aliases=True, raise AssertionError("%s.%s = %s is not %s." % (module, alias, obj, original)) if not seen: - warnings.warn('Setting %s.%s to %s. There was no previous definition, probably patching the wrong module.' % ( - target, name, replacement - )) + warnings.warn( + 'Setting %s.%s to %s. There was no previous definition, probably patching the wrong module.' % (target, name, replacement) + ) logdebug("= saving %s on %s.%s ...", replacement, target, name) setattr(module, name, replacement) rollback.merge(lambda: setattr(module, name, original)) @@ -770,7 +658,8 @@ def patch_module_function(module, target, aspect, force_name=None, bag=BrokenBag :returns: An :obj:`aspectlib.Rollback` object. """ - logdebug("patch_module_function (module=%s, target=%s, aspect=%s, force_name=%s, **options=%s", - module, target, aspect, force_name, options) + logdebug( + "patch_module_function (module=%s, target=%s, aspect=%s, force_name=%s, **options=%s", module, target, aspect, force_name, options + ) name = force_name or target.__name__ return patch_module(module, name, _checked_apply(aspect, target, module=module), original=target, **options) diff --git a/src/aspectlib/py2chainmap.py b/src/aspectlib/py2chainmap.py deleted file mode 100644 index 70d56c6..0000000 --- a/src/aspectlib/py2chainmap.py +++ /dev/null @@ -1,106 +0,0 @@ -from collections import MutableMapping - -__all__ = 'ChainMap', - - -class ChainMap(MutableMapping): - ''' A ChainMap groups multiple dicts (or other mappings) together - to create a single, updateable view. - - The underlying mappings are stored in a list. That list is public and can - accessed or updated using the *maps* attribute. There is no other state. - - Lookups search the underlying mappings successively until a key is found. - In contrast, writes, updates, and deletions only operate on the first - mapping. - - ''' - - def __init__(self, *maps): - '''Initialize a ChainMap by setting *maps* to the given mappings. - If no mappings are provided, a single empty dictionary is used. - - ''' - self.maps = list(maps) or [{}] # always at least one map - - def __missing__(self, key): - raise KeyError(key) - - def __getitem__(self, key): - for mapping in self.maps: - try: - return mapping[key] # can't use 'key in mapping' with defaultdict - except KeyError: - pass - return self.__missing__(key) # support subclasses that define __missing__ - - def get(self, key, default=None): - return self[key] if key in self else default - - def __len__(self): - return len(set().union(*self.maps)) # reuses stored hash values if possible - - def __iter__(self): - return iter(set().union(*self.maps)) - - def __contains__(self, key): - return any(key in m for m in self.maps) - - def __bool__(self): - return any(self.maps) - - def __repr__(self): - return '{0.__class__.__name__}({1})'.format( - self, ', '.join(map(repr, self.maps))) - - @classmethod - def fromkeys(cls, iterable, *args): - 'Create a ChainMap with a single dict created from the iterable.' - return cls(dict.fromkeys(iterable, *args)) - - def copy(self): - 'New ChainMap or subclass with a new copy of maps[0] and refs to maps[1:]' - return self.__class__(self.maps[0].copy(), *self.maps[1:]) - - __copy__ = copy - - def new_child(self, m=None): # like Django's Context.push() - ''' - New ChainMap with a new map followed by all previous maps. If no - map is provided, an empty dict is used. - ''' - if m is None: - m = {} - return self.__class__(m, *self.maps) - - @property - def parents(self): # like Django's Context.pop() - 'New ChainMap from maps[1:].' - return self.__class__(*self.maps[1:]) - - def __setitem__(self, key, value): - self.maps[0][key] = value - - def __delitem__(self, key): - try: - del self.maps[0][key] - except KeyError: - raise KeyError('Key not found in the first mapping: {!r}'.format(key)) - - def popitem(self): - 'Remove and return an item pair from maps[0]. Raise KeyError is maps[0] is empty.' - try: - return self.maps[0].popitem() - except KeyError: - raise KeyError('No keys found in the first mapping.') - - def pop(self, key, *args): - 'Remove *key* from maps[0] and return its value. Raise KeyError if *key* not in maps[0].' - try: - return self.maps[0].pop(key, *args) - except KeyError: - raise KeyError('Key not found in the first mapping: {!r}'.format(key)) - - def clear(self): - 'Clear maps[0], leaving maps[1:] intact.' - self.maps[0].clear() diff --git a/src/aspectlib/py2ordereddict.py b/src/aspectlib/py2ordereddict.py deleted file mode 100644 index 2d1d813..0000000 --- a/src/aspectlib/py2ordereddict.py +++ /dev/null @@ -1,128 +0,0 @@ -# Copyright (c) 2009 Raymond Hettinger -# -# Permission is hereby granted, free of charge, to any person -# obtaining a copy of this software and associated documentation files -# (the "Software"), to deal in the Software without restriction, -# including without limitation the rights to use, copy, modify, merge, -# publish, distribute, sublicense, and/or sell copies of the Software, -# and to permit persons to whom the Software is furnished to do so, -# subject to the following conditions: -# -# The above copyright notice and this permission notice shall be -# included in all copies or substantial portions of the Software. -# -# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, -# EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES -# OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND -# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT -# HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, -# WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING -# FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR -# OTHER DEALINGS IN THE SOFTWARE. - -from UserDict import DictMixin - - -class OrderedDict(dict, DictMixin): - - def __init__(self, *args, **kwds): - if len(args) > 1: - raise TypeError('expected at most 1 arguments, got %d' % len(args)) - try: - self.__end - except AttributeError: - self.clear() - self.update(*args, **kwds) - - def clear(self): - self.__end = end = [] - end += [None, end, end] # sentinel node for doubly linked list - self.__map = {} # key --> [key, prev, next] - dict.clear(self) - - def __setitem__(self, key, value): - if key not in self: - end = self.__end - curr = end[1] - curr[2] = end[1] = self.__map[key] = [key, curr, end] - dict.__setitem__(self, key, value) - - def __delitem__(self, key): - dict.__delitem__(self, key) - key, prev, next = self.__map.pop(key) - prev[2] = next - next[1] = prev - - def __iter__(self): - end = self.__end - curr = end[2] - while curr is not end: - yield curr[0] - curr = curr[2] - - def __reversed__(self): - end = self.__end - curr = end[1] - while curr is not end: - yield curr[0] - curr = curr[1] - - def popitem(self, last=True): - if not self: - raise KeyError('dictionary is empty') - if last: - key = reversed(self).next() - else: - key = iter(self).next() - value = self.pop(key) - return key, value - - def __reduce__(self): - items = [[k, self[k]] for k in self] - tmp = self.__map, self.__end - del self.__map, self.__end - inst_dict = vars(self).copy() - self.__map, self.__end = tmp - if inst_dict: - return (self.__class__, (items,), inst_dict) - return self.__class__, (items,) - - def keys(self): - return list(self) - - setdefault = DictMixin.setdefault - update = DictMixin.update - pop = DictMixin.pop - values = DictMixin.values - items = DictMixin.items - iterkeys = DictMixin.iterkeys - itervalues = DictMixin.itervalues - iteritems = DictMixin.iteritems - - def __repr__(self): - if not self: - return '%s()' % (self.__class__.__name__,) - return '%s(%r)' % (self.__class__.__name__, self.items()) - - def copy(self): - return self.__class__(self) - - @classmethod - def fromkeys(cls, iterable, value=None): - d = cls() - for key in iterable: - d[key] = value - return d - - def __eq__(self, other): - if isinstance(other, OrderedDict): - if len(self) != len(other): - return False - for p, q in zip(self.items(), other.items()): - if p != q: - return False - return True - return dict.__eq__(self, other) - - def __ne__(self, other): - return not self == other diff --git a/src/aspectlib/py35support.py b/src/aspectlib/py35support.py index 27e2c9a..22f8b85 100644 --- a/src/aspectlib/py35support.py +++ b/src/aspectlib/py35support.py @@ -1,5 +1,3 @@ -from __future__ import print_function - import sys from inspect import iscoroutinefunction from inspect import isgenerator @@ -60,6 +58,7 @@ async def advising_asyncgenerator_wrapper_py35(*args, **kwargs): raise UnacceptableAdvice("Unknown advice %s" % advice) finally: advisor.close() + return mimic(advising_asyncgenerator_wrapper_py35, cutpoint_function) @@ -101,4 +100,5 @@ def advising_generator_wrapper_py35(*args, **kwargs): raise UnacceptableAdvice("Unknown advice %s" % advice) finally: advisor.close() + return mimic(advising_generator_wrapper_py35, cutpoint_function) diff --git a/src/aspectlib/py3support.py b/src/aspectlib/py3support.py index db9f907..beaee11 100644 --- a/src/aspectlib/py3support.py +++ b/src/aspectlib/py3support.py @@ -1,5 +1,3 @@ -from __future__ import print_function - import sys from inspect import isgenerator from inspect import isgeneratorfunction @@ -54,4 +52,5 @@ def advising_generator_wrapper_py3(*args, **kwargs): raise UnacceptableAdvice("Unknown advice %s" % advice) finally: advisor.close() + return mimic(advising_generator_wrapper_py3, cutpoint_function) diff --git a/src/aspectlib/test.py b/src/aspectlib/test.py index e8c5464..8f27181 100644 --- a/src/aspectlib/test.py +++ b/src/aspectlib/test.py @@ -33,14 +33,9 @@ from _dummy_thread import allocate_lock except ImportError: from _thread import allocate_lock -try: - from collections import OrderedDict -except ImportError: - from .py2ordereddict import OrderedDict -try: - from collections import ChainMap -except ImportError: - from .py2chainmap import ChainMap + +from collections import ChainMap +from collections import OrderedDict __all__ = 'mock', 'record', "Story" @@ -111,6 +106,7 @@ class LogCapture(object): Added ``messages`` property. Changed ``calls`` to retrun the level as a string (instead of int). """ + def __init__(self, logger, level='DEBUG'): self._logger = logger self._level = nameToLevel[level] @@ -121,7 +117,7 @@ def __enter__(self): self._rollback = weave( self._logger, record(callback=self._callback, extended=True, iscalled=True), - methods=('debug', 'info', 'warning', 'error', 'exception', 'critical', 'log') + methods=('debug', 'info', 'warning', 'error', 'exception', 'critical', 'log'), ) return self @@ -144,12 +140,7 @@ def _callback(self, _binding, qualname, args, _kwargs): message, args = args[0], () if level >= self._level: - self._calls.append(( - message % args if args else message, - message, - args, - getLevelName(level) - )) + self._calls.append((message % args if args else message, message, args, getLevelName(level))) @property def calls(self): @@ -164,19 +155,16 @@ def has(self, message, *args, **kwargs): assert not kwargs, "Unexpected arguments: %s" % kwargs for call_final_message, call_message, call_args, call_level in self._calls: if level is None or level == call_level: - if ( - message == call_message and args == call_args - if args else - message == call_final_message or message == call_message - ): + if message == call_message and args == call_args if args else message == call_final_message or message == call_message: return True return False def assertLogged(self, message, *args, **kwargs): if not self.has(message, *args, **kwargs): - raise AssertionError("There's no such message %r (with args %r) logged on %s. Logged messages where: %s" % ( - message, args, self._logger, self.calls - )) + raise AssertionError( + "There's no such message %r (with args %r) logged on %s. Logged messages where: %s" + % (message, args, self._logger, self.calls) + ) class _RecordingFunctionWrapper(object): @@ -186,8 +174,7 @@ class _RecordingFunctionWrapper(object): See :obj:`aspectlib.test.record` for arguments. """ - def __init__(self, wrapped, iscalled=True, calls=None, callback=None, extended=False, results=False, - recurse_lock=None, binding=None): + def __init__(self, wrapped, iscalled=True, calls=None, callback=None, extended=False, results=False, recurse_lock=None, binding=None): assert not results or iscalled, "`iscalled` must be True if `results` is True" mimic(self, wrapped) self.__wrapped = wrapped @@ -228,13 +215,9 @@ def __record(self, args, kwargs, *response): self.__callback(self.__binding, qualname(self), args, kwargs, *response) if self.calls is not None: if self.__extended: - self.calls.append((ResultEx if response else CallEx)( - self.__binding, qualname(self), args, kwargs, *response - )) + self.calls.append((ResultEx if response else CallEx)(self.__binding, qualname(self), args, kwargs, *response)) else: - self.calls.append((Result if response else Call)( - self.__binding, args, kwargs, *response - )) + self.calls.append((Result if response else Call)(self.__binding, args, kwargs, *response)) def __get__(self, instance, owner): return _RecordingFunctionWrapper( @@ -307,11 +290,7 @@ def record(func=None, recurse_lock_factory=allocate_lock, **options): Added `extended` option. """ if func: - return _RecordingFunctionWrapper( - func, - recurse_lock=recurse_lock_factory(), - **options - ) + return _RecordingFunctionWrapper(func, recurse_lock=recurse_lock_factory(), **options) else: return partial(record, **options) @@ -334,14 +313,73 @@ def __unsupported__(self, *args): raise TypeError("Unsupported operation. Only `==` (for results) and `**` (for exceptions) can be used.") for mm in ( - '__add__', '__sub__', '__mul__', '__floordiv__', '__mod__', '__divmod__', '__lshift__', '__rshift__', '__and__', - '__xor__', '__or__', '__div__', '__truediv__', '__radd__', '__rsub__', '__rmul__', '__rdiv__', '__rtruediv__', - '__rfloordiv__', '__rmod__', '__rdivmod__', '__rpow__', '__rlshift__', '__rrshift__', '__rand__', '__rxor__', - '__ror__', '__iadd__', '__isub__', '__imul__', '__idiv__', '__itruediv__', '__ifloordiv__', '__imod__', - '__ipow__', '__ilshift__', '__irshift__', '__iand__', '__ixor__', '__ior__', '__neg__', '__pos__', '__abs__', - '__invert__', '__complex__', '__int__', '__long__', '__float__', '__oct__', '__hex__', '__index__', - '__coerce__', '__getslice__', '__setslice__', '__delslice__', '__len__', '__getitem__', '__reversed__', - '__contains__', '__call__', '__lt__', '__le__', '__ne__', '__gt__', '__ge__', '__cmp__', '__rcmp__', + '__add__', + '__sub__', + '__mul__', + '__floordiv__', + '__mod__', + '__divmod__', + '__lshift__', + '__rshift__', + '__and__', + '__xor__', + '__or__', + '__div__', + '__truediv__', + '__radd__', + '__rsub__', + '__rmul__', + '__rdiv__', + '__rtruediv__', + '__rfloordiv__', + '__rmod__', + '__rdivmod__', + '__rpow__', + '__rlshift__', + '__rrshift__', + '__rand__', + '__rxor__', + '__ror__', + '__iadd__', + '__isub__', + '__imul__', + '__idiv__', + '__itruediv__', + '__ifloordiv__', + '__imod__', + '__ipow__', + '__ilshift__', + '__irshift__', + '__iand__', + '__ixor__', + '__ior__', + '__neg__', + '__pos__', + '__abs__', + '__invert__', + '__complex__', + '__int__', + '__long__', + '__float__', + '__oct__', + '__hex__', + '__index__', + '__coerce__', + '__getslice__', + '__setslice__', + '__delslice__', + '__len__', + '__getitem__', + '__reversed__', + '__contains__', + '__call__', + '__lt__', + '__le__', + '__ne__', + '__gt__', + '__ge__', + '__cmp__', + '__rcmp__', '__nonzero__', ): exec("%s = __unsupported__" % mm) @@ -369,12 +407,15 @@ def __call__(self, *args, **kwargs): return StoryResultWrapper(partial(self._handle, self._binding, self._name, args, kwargs)) def __get__(self, binding, owner): - return mimic(type(self)( - self._wrapped.__get__(binding, owner) if hasattr(self._wrapped, '__get__') else self._wrapped, - handle=self._handle, - binding=binding, - owner=owner, - ), self) + return mimic( + type(self)( + self._wrapped.__get__(binding, owner) if hasattr(self._wrapped, '__get__') else self._wrapped, + handle=self._handle, + binding=binding, + owner=owner, + ), + self, + ) class _ReplayFunctionWrapper(_StoryFunctionWrapper): @@ -402,12 +443,7 @@ def __init__(self, target, **options): def _make_key(self, binding, name, args, kwargs): if binding is not None: binding, _ = self._ids[id(binding)] - return ( - binding, - name, - ', '.join(repr_ex(i) for i in args), - ', '.join("%s=%s" % (k, repr_ex(v)) for k, v in kwargs.items()) - ) + return (binding, name, ', '.join(repr_ex(i) for i in args), ', '.join("%s=%s" % (k, repr_ex(v)) for k, v in kwargs.items())) def _tag_result(self, name, result): if isinstance(result, _Binds): @@ -423,21 +459,20 @@ def _tag_result(self, name, result): def _handle(self, binding, name, args, kwargs, result): pk = self._make_key(binding, name, args, kwargs) result = self._tag_result(name, result) - assert pk not in self._calls or self._calls[pk] == result, ( - "Story creation inconsistency. There is already a result cached for " - "binding:%r name:%r args:%r kwargs:%r and it's: %r." % ( - binding, name, args, kwargs, self._calls[pk] - ) + assert ( + pk not in self._calls or self._calls[pk] == result + ), "Story creation inconsistency. There is already a result cached for " "binding:%r name:%r args:%r kwargs:%r and it's: %r." % ( + binding, + name, + args, + kwargs, + self._calls[pk], ) self._calls[pk] = result def __enter__(self): self._options.setdefault('methods', ALL_METHODS) - self.__entanglement = weave( - self._target, - partial(self._FunctionWrapper, handle=self._handle), - **self._options - ) + self.__entanglement = weave(self._target, partial(self._FunctionWrapper, handle=self._handle), **self._options) return self def __exit__(self, *args): @@ -452,46 +487,47 @@ def __exit__(self, *args): class Story(_RecordingBase): """ - This a simple yet flexible tool that can do "capture-replay mocking" or "test doubles" [1]_. It leverages - ``aspectlib``'s powerful :obj:`weaver `. + This a simple yet flexible tool that can do "capture-replay mocking" or "test doubles" [1]_. It leverages + ``aspectlib``'s powerful :obj:`weaver `. - Args: - target (same as for :obj:`aspectlib.weave`): - Targets to weave in the `story`/`replay` transactions. - subclasses (bool): - If ``True``, subclasses of target are weaved. *Only available for classes* - aliases (bool): - If ``True``, aliases of target are replaced. - lazy (bool): - If ``True`` only target's ``__init__`` method is patched, the rest of the methods are patched after ``__init__`` - is called. *Only available for classes*. - methods (list or regex or string): Methods from target to patch. *Only available for classes* + Args: + target (same as for :obj:`aspectlib.weave`): + Targets to weave in the `story`/`replay` transactions. + subclasses (bool): + If ``True``, subclasses of target are weaved. *Only available for classes* + aliases (bool): + If ``True``, aliases of target are replaced. + lazy (bool): + If ``True`` only target's ``__init__`` method is patched, the rest of the methods are patched after ``__init__`` + is called. *Only available for classes*. + methods (list or regex or string): Methods from target to patch. *Only available for classes* - The ``Story`` allows some testing patterns that are hard to do with other tools: + The ``Story`` allows some testing patterns that are hard to do with other tools: - * **Proxied mocks**: partially mock `objects` and `modules` so they are called normally if the request is unknown. - * **Stubs**: completely mock `objects` and `modules`. Raise errors if the request is unknown. + * **Proxied mocks**: partially mock `objects` and `modules` so they are called normally if the request is unknown. + * **Stubs**: completely mock `objects` and `modules`. Raise errors if the request is unknown. - The ``Story`` works in two of transactions: + The ``Story`` works in two of transactions: - * **The story**: You describe what calls you want to mocked. Initially you don't need to write this. Example: + * **The story**: You describe what calls you want to mocked. Initially you don't need to write this. Example: - :: + :: - >>> import mymod - >>> with Story(mymod) as story: - ... mymod.func('some arg') == 'some result' - ... mymod.func('bad arg') ** ValueError("can't use this") + >>> import mymod + >>> with Story(mymod) as story: + ... mymod.func('some arg') == 'some result' + ... mymod.func('bad arg') ** ValueError("can't use this") - * **The replay**: You run the code uses the interfaces mocked in the `story`. The :obj:`replay - ` always starts from a `story` instance. + * **The replay**: You run the code uses the interfaces mocked in the `story`. The :obj:`replay + ` always starts from a `story` instance. - .. versionchanged:: 0.9.0 + .. versionchanged:: 0.9.0 - Added in. + Added in. - .. [1] http://www.martinfowler.com/bliki/TestDouble.html + .. [1] http://www.martinfowler.com/bliki/TestDouble.html """ + _FunctionWrapper = _StoryFunctionWrapper def __init__(self, *args, **kwargs): @@ -548,10 +584,7 @@ def logged_eval(value, context): try: return eval(value, *context) except: # noqa - logexception("Failed to evaluate %r.\nContext:\n%s", value, ''.join(format_stack( - f=_getframe(1), - limit=15 - ))) + logexception("Failed to evaluate %r.\nContext:\n%s", value, ''.join(format_stack(f=_getframe(1), limit=15))) raise @@ -562,6 +595,7 @@ class Replay(_RecordingBase): This object should be created by :obj:`Story `'s :obj:`replay ` method. """ + _FunctionWrapper = _ReplayFunctionWrapper def __init__(self, play, proxy=True, strict=True, dump=True, recurse_lock=False, **options): @@ -613,10 +647,7 @@ def _unexpected(self, _missing=False): expected, actual = self._actual, self._expected else: actual, expected = self._actual, self._expected - return ''.join(_format_calls(OrderedDict( - (pk, val) for pk, val in actual.items() - if pk not in expected or val != expected.get(pk) - ))) + return ''.join(_format_calls(OrderedDict((pk, val) for pk, val in actual.items() if pk not in expected or val != expected.get(pk)))) @property def unexpected(self): diff --git a/src/aspectlib/utils.py b/src/aspectlib/utils.py index 9e0837e..c936911 100644 --- a/src/aspectlib/utils.py +++ b/src/aspectlib/utils.py @@ -1,5 +1,3 @@ -from __future__ import print_function - import logging import os import platform @@ -12,9 +10,7 @@ RegexType = type(re.compile("")) PY3 = sys.version_info[0] == 3 -PY37plus = PY3 and sys.version_info[1] >= 7 -PY2 = sys.version_info[0] == 2 -PY26 = PY2 and sys.version_info[1] == 6 +PY310 = PY3 and sys.version_info[1] >= 10 PYPY = platform.python_implementation() == 'PyPy' if PY3: @@ -43,6 +39,7 @@ def log_wrapper(*args): logging.logProcesses = logProcesses logging.logThreads = logThreads logging.logMultiprocessing = logMultiprocessing + return log_wrapper @@ -61,6 +58,7 @@ def qualname(obj): def force_bind(func): def bound(self, *args, **kwargs): # pylint: disable=W0613 return func(*args, **kwargs) + bound.__name__ = func.__name__ bound.__doc__ = func.__doc__ return bound @@ -87,6 +85,7 @@ def __repr__(self): return "%s" % self.name else: return "%s: %s" % (self.name, self.__doc__) + __str__ = __repr__ @@ -94,13 +93,17 @@ def container(name): def __init__(self, value): self.value = value - return type(name, (object,), { - '__slots__': 'value', - '__init__': __init__, - '__str__': lambda self: "%s(%s)" % (name, self.value), - '__repr__': lambda self: "%s(%r)" % (name, self.value), - '__eq__': lambda self, other: type(self) is type(other) and self.value == other.value, - }) + return type( + name, + (object,), + { + '__slots__': 'value', + '__init__': __init__, + '__str__': lambda self: "%s(%s)" % (name, self.value), + '__repr__': lambda self: "%s(%r)" % (name, self.value), + '__eq__': lambda self, other: type(self) is type(other) and self.value == other.value, + }, + ) def mimic(wrapper, func, module=None): @@ -125,9 +128,8 @@ def mimic(wrapper, func, module=None): set: lambda obj, aliases: "set([%s])" % ', '.join(repr_ex(i) for i in obj), frozenset: lambda obj, aliases: "set([%s])" % ', '.join(repr_ex(i) for i in obj), deque: lambda obj, aliases: "collections.deque([%s])" % ', '.join(repr_ex(i) for i in obj), - dict: lambda obj, aliases: "{%s}" % ', '.join( - "%s: %s" % (repr_ex(k), repr_ex(v)) for k, v in (obj.items() if PY3 else obj.iteritems()) - ), + dict: lambda obj, aliases: "{%s}" + % ', '.join("%s: %s" % (repr_ex(k), repr_ex(v)) for k, v in (obj.items() if PY3 else obj.iteritems())), } @@ -135,10 +137,7 @@ def _make_fixups(): for obj in ('os.stat_result', 'grp.struct_group', 'pwd.struct_passwd'): mod, attr = obj.split('.') try: - yield getattr(__import__(mod), attr), lambda obj, aliases, prefix=obj: "%s(%r)" % ( - prefix, - obj.__reduce__()[1][0] - ) + yield getattr(__import__(mod), attr), lambda obj, aliases, prefix=obj: "%s(%r)" % (prefix, obj.__reduce__()[1][0]) except ImportError: continue diff --git a/tests/mymod.py b/tests/mymod.py index cc8a3d8..9855e10 100644 --- a/tests/mymod.py +++ b/tests/mymod.py @@ -1,6 +1,3 @@ -from __future__ import print_function - - def func(arg): print("Got", arg, "in the real code!") diff --git a/tests/test_aspectlib.py b/tests/test_aspectlib.py index 6146e86..a50558f 100644 --- a/tests/test_aspectlib.py +++ b/tests/test_aspectlib.py @@ -1,7 +1,4 @@ # encoding: utf8 -from __future__ import print_function - -import pytest from pytest import raises import aspectlib @@ -401,27 +398,33 @@ def test_weave_str_bad_target(): def test_weave_str_target(): with aspectlib.weave('test_pkg1.test_pkg2.test_mod.target', mock('foobar')): from test_pkg1.test_pkg2.test_mod import target + assert target() == 'foobar' from test_pkg1.test_pkg2.test_mod import target + assert target() is None def test_weave_str_class_target(): with aspectlib.weave('test_pkg1.test_pkg2.test_mod.Stuff', mock('foobar')): from test_pkg1.test_pkg2.test_mod import Stuff + assert Stuff().meth() == 'foobar' from test_pkg1.test_pkg2.test_mod import Stuff + assert Stuff().meth() is None def test_weave_str_class_meth_target(): with aspectlib.weave('test_pkg1.test_pkg2.test_mod.Stuff.meth', mock('foobar')): from test_pkg1.test_pkg2.test_mod import Stuff + assert Stuff().meth() == 'foobar' from test_pkg1.test_pkg2.test_mod import Stuff + assert Stuff().meth() is None @@ -439,10 +442,14 @@ def test_weave_wrong_module(): with aspectlib.weave('warnings.warn', record(calls=calls)): aspectlib.weave(AliasedGlobal, mock('stuff'), lazy=True) assert calls == [ - (None, - ("Setting test_aspectlib.MissingGlobal to . " - "There was no previous definition, probably patching the wrong module.",), - {}) + ( + None, + ( + "Setting test_aspectlib.MissingGlobal to . " + "There was no previous definition, probably patching the wrong module.", + ), + {}, + ) ] @@ -457,19 +464,6 @@ def test_weave_no_aliases(): assert module_func2 is module_func3 -@pytest.mark.skipif('aspectlib.PY3') -def test_weave_class_meth_no_aliases(): - with aspectlib.weave(Global.meth, mock('stuff'), aliases=False, lazy=True): - assert Global().meth() == 'stuff' - assert Global2 is not Global - assert Global2().meth() == 'base' - - assert Global().meth() == 'base' - assert Global2 is Global - assert Global2().meth() == 'base' - - -@pytest.mark.skipif('aspectlib.PY2') def test_weave_class_meth_no_aliases_unsupported_on_py3(): with aspectlib.weave(Global.meth, mock('stuff')): assert Global().meth() == 'stuff' @@ -540,7 +534,6 @@ def aspect(self): assert inst.foo == 'stuff' -@pytest.mark.skipif('aspectlib.PY2') def test_weave_legacy_instance(): @aspectlib.Aspect def aspect(self): @@ -657,7 +650,7 @@ def test_weave_class(): @aspectlib.Aspect def aspect(*args): history.append(args) - args += ':)', + args += (':)',) yield aspectlib.Proceed(*args) yield aspectlib.Return('bar') @@ -754,7 +747,7 @@ def test_weave_class_slots(): @aspectlib.Aspect def aspect(*args): history.append(args) - args += ':)', + args += (':)',) yield aspectlib.Proceed(*args) yield aspectlib.Return('bar') @@ -860,7 +853,7 @@ def test_weave_class_on_init(): @aspectlib.Aspect def aspect(*args): history.append(args) - args += ':)', + args += (':)',) yield aspectlib.Proceed(*args) yield aspectlib.Return('bar') @@ -953,7 +946,7 @@ def test_weave_class_old_style(): @aspectlib.Aspect def aspect(*args): history.append(args) - args += ':)', + args += (':)',) yield aspectlib.Proceed(*args) yield aspectlib.Return('bar') @@ -1190,15 +1183,6 @@ def test_weave_subclass_meth_manual(): assert Sub().meth() == 'base' -@pytest.mark.skipif('aspectlib.PY3') -def test_weave_subclass_meth_auto(): - with aspectlib.weave(Sub.meth, mock('foobar'), lazy=True): - assert Sub().meth() == 'foobar' - - assert Sub().meth() == 'base' - - -@pytest.mark.skipif('aspectlib.PY2') def test_weave_subclass_meth_auto2(): with aspectlib.weave(Sub.meth, mock('foobar')): assert Sub().meth() == 'foobar' @@ -1227,22 +1211,13 @@ def _internal(): pass -if aspectlib.PY3: - exec(u"""# encoding: utf8 - def ăbc(): pass + def test_ăbc(): with aspectlib.weave('test_aspectlib.ăbc', mock('stuff')): assert ăbc() == 'stuff' -""") -else: - def test_py2_invalid_unicode_in_string_target(): - raises(SyntaxError, aspectlib.weave, 'os.ăa', mock(None)) - raises(SyntaxError, aspectlib.weave, u'os.ăa', mock(None)) - raises(SyntaxError, aspectlib.weave, 'os.aă', mock(None)) - raises(SyntaxError, aspectlib.weave, u'os.aă', mock(None)) def test_invalid_string_target(): @@ -1471,14 +1446,12 @@ def func(): def test_weave_module(strmod=None): calls = [] from test_pkg1.test_pkg2 import test_mod + with aspectlib.weave(strmod or test_mod, record(calls=calls, extended=True)): test_mod.target() obj = test_mod.Stuff() obj.meth() - assert calls == [ - (None, 'test_pkg1.test_pkg2.test_mod.target', (), {}), - (obj, 'test_pkg1.test_pkg2.test_mod.meth', (), {}) - ] + assert calls == [(None, 'test_pkg1.test_pkg2.test_mod.target', (), {}), (obj, 'test_pkg1.test_pkg2.test_mod.meth', (), {})] def test_weave_module_as_str(): diff --git a/tests/test_aspectlib_debug.py b/tests/test_aspectlib_debug.py index 94eff55..4d91c46 100644 --- a/tests/test_aspectlib_debug.py +++ b/tests/test_aspectlib_debug.py @@ -1,5 +1,3 @@ -from __future__ import print_function - import logging import re import sys @@ -15,10 +13,12 @@ except ImportError: from io import StringIO -LOG_TEST_SIMPLE = r'''^some_meth\(1, 2, 3, a=4\) +<<< .*tests/test_aspectlib_debug.py:\d+:test_simple.* -some_meth => \.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\. !"#\$%&\'\(\)\*\+,-\./0123456789:;<=>\?@''' \ -r'''ABCDEFGHIJKLMNOPQRSTUVWXYZ\[\\\]\^_`abcdefghijklmnopqrstuvwxyz\{\|\}~\.+ +LOG_TEST_SIMPLE = ( + r'''^some_meth\(1, 2, 3, a=4\) +<<< .*tests/test_aspectlib_debug.py:\d+:test_simple.* +some_meth => \.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\. !"#\$%&\'\(\)\*\+,-\./0123456789:;<=>\?@''' + r'''ABCDEFGHIJKLMNOPQRSTUVWXYZ\[\\\]\^_`abcdefghijklmnopqrstuvwxyz\{\|\}~\.+ $''' +) def some_meth(*_args, **_kwargs): @@ -61,6 +61,7 @@ def test_fail_to_log(): @aspectlib.debug.log(print_to="crap") def foo(): pass + foo() @@ -73,66 +74,75 @@ def test_logging_works(): @aspectlib.debug.log def foo(): pass + foo() assert re.match(r'foo\(\) +<<<.*\nfoo => None\n', buf.getvalue()) def test_attributes(): buf = StringIO() - with aspectlib.weave(MyStuff, aspectlib.debug.log( - print_to=buf, - stacktrace=10, - attributes=('foo', 'bar()') - ), methods='(?!bar)(?!__.*__$)'): + with aspectlib.weave( + MyStuff, aspectlib.debug.log(print_to=buf, stacktrace=10, attributes=('foo', 'bar()')), methods='(?!bar)(?!__.*__$)' + ): MyStuff('bar').stuff() print(buf.getvalue()) - assert re.match(r"^\{test_aspectlib_debug.MyStuff foo='bar' bar='foo'\}.stuff\(\) +<<< .*tests/test_aspectlib_debug.py:\d+:" - r"test_attributes.*\n\{test_aspectlib_debug.MyStuff foo='bar' bar='foo'\}.stuff => bar\n$", buf.getvalue()) + assert re.match( + r"^\{test_aspectlib_debug.MyStuff foo='bar' bar='foo'\}.stuff\(\) +<<< .*tests/test_aspectlib_debug.py:\d+:" + r"test_attributes.*\n\{test_aspectlib_debug.MyStuff foo='bar' bar='foo'\}.stuff => bar\n$", + buf.getvalue(), + ) MyStuff('bar').stuff() - assert re.match(r"^\{test_aspectlib_debug.MyStuff foo='bar' bar='foo'\}.stuff\(\) +<<< .*tests/test_aspectlib_debug.py:\d+:" - r"test_attributes.*\n\{test_aspectlib_debug.MyStuff foo='bar' bar='foo'\}.stuff => bar\n$", buf.getvalue()) + assert re.match( + r"^\{test_aspectlib_debug.MyStuff foo='bar' bar='foo'\}.stuff\(\) +<<< .*tests/test_aspectlib_debug.py:\d+:" + r"test_attributes.*\n\{test_aspectlib_debug.MyStuff foo='bar' bar='foo'\}.stuff => bar\n$", + buf.getvalue(), + ) def test_no_stack(): buf = StringIO() - with aspectlib.weave(MyStuff, aspectlib.debug.log( - print_to=buf, - stacktrace=None, - attributes=('foo', 'bar()') - ), methods='(?!bar)(?!__.*__$)'): + with aspectlib.weave( + MyStuff, aspectlib.debug.log(print_to=buf, stacktrace=None, attributes=('foo', 'bar()')), methods='(?!bar)(?!__.*__$)' + ): MyStuff('bar').stuff() print(buf.getvalue()) - assert "{test_aspectlib_debug.MyStuff foo='bar' bar='foo'}.stuff()\n" \ - "{test_aspectlib_debug.MyStuff foo='bar' bar='foo'}.stuff => bar\n" == buf.getvalue() + assert ( + "{test_aspectlib_debug.MyStuff foo='bar' bar='foo'}.stuff()\n" + "{test_aspectlib_debug.MyStuff foo='bar' bar='foo'}.stuff => bar\n" == buf.getvalue() + ) def test_attributes_old_style(): buf = StringIO() - with aspectlib.weave(OldStuff, aspectlib.debug.log( - print_to=buf, - stacktrace=10, - attributes=('foo', 'bar()') - ), methods='(?!bar)(?!__.*__$)'): + with aspectlib.weave( + OldStuff, aspectlib.debug.log(print_to=buf, stacktrace=10, attributes=('foo', 'bar()')), methods='(?!bar)(?!__.*__$)' + ): OldStuff('bar').stuff() print(repr(buf.getvalue())) - assert re.match(r"^\{test_aspectlib_debug.OldStuff foo='bar' bar='foo'\}.stuff\(\) +<<< .*tests/test_aspectlib_debug.py:\d+:" - r"test_attributes.*\n\{test_aspectlib_debug.OldStuff foo='bar' bar='foo'\}.stuff => bar\n$", buf.getvalue()) + assert re.match( + r"^\{test_aspectlib_debug.OldStuff foo='bar' bar='foo'\}.stuff\(\) +<<< .*tests/test_aspectlib_debug.py:\d+:" + r"test_attributes.*\n\{test_aspectlib_debug.OldStuff foo='bar' bar='foo'\}.stuff => bar\n$", + buf.getvalue(), + ) MyStuff('bar').stuff() - assert re.match(r"^\{test_aspectlib_debug.OldStuff foo='bar' bar='foo'\}.stuff\(\) +<<< .*tests/test_aspectlib_debug.py:\d+:" - r"test_attributes.*\n\{test_aspectlib_debug.OldStuff foo='bar' bar='foo'\}.stuff => bar\n$", buf.getvalue()) + assert re.match( + r"^\{test_aspectlib_debug.OldStuff foo='bar' bar='foo'\}.stuff\(\) +<<< .*tests/test_aspectlib_debug.py:\d+:" + r"test_attributes.*\n\{test_aspectlib_debug.OldStuff foo='bar' bar='foo'\}.stuff => bar\n$", + buf.getvalue(), + ) def test_no_stack_old_style(): buf = StringIO() - with aspectlib.weave(OldStuff, aspectlib.debug.log( - print_to=buf, - stacktrace=None, - attributes=('foo', 'bar()') - ), methods='(?!bar)(?!__.*__$)'): + with aspectlib.weave( + OldStuff, aspectlib.debug.log(print_to=buf, stacktrace=None, attributes=('foo', 'bar()')), methods='(?!bar)(?!__.*__$)' + ): OldStuff('bar').stuff() print(buf.getvalue()) - assert "{test_aspectlib_debug.OldStuff foo='bar' bar='foo'}.stuff()\n" \ - "{test_aspectlib_debug.OldStuff foo='bar' bar='foo'}.stuff => bar\n" == buf.getvalue() + assert ( + "{test_aspectlib_debug.OldStuff foo='bar' bar='foo'}.stuff()\n" + "{test_aspectlib_debug.OldStuff foo='bar' bar='foo'}.stuff => bar\n" == buf.getvalue() + ) @pytest.mark.skipif(sys.version_info < (2, 7), reason="No weakref.WeakSet on Python<=2.6") diff --git a/tests/test_aspectlib_py23.py b/tests/test_aspectlib_py23.py deleted file mode 100644 index 6a65209..0000000 --- a/tests/test_aspectlib_py23.py +++ /dev/null @@ -1,169 +0,0 @@ -# encoding: utf8 -from __future__ import print_function - -import pytest - -import aspectlib -from aspectlib.utils import PY37plus - -pytestmark = pytest.mark.skipif(PY37plus, reason="Tests are incompatible with PEP-479") - - -def test_aspect_on_generator_result(): - result = [] - - @aspectlib.Aspect - def aspect(): - result.append((yield aspectlib.Proceed)) - - @aspect - def func(): - yield 'something' - raise StopIteration('value') - - assert list(func()) == ['something'] - assert result == ['value'] - - -def test_aspect_on_coroutine(): - hist = [] - - @aspectlib.Aspect - def aspect(): - try: - hist.append('before') - hist.append((yield aspectlib.Proceed)) - hist.append('after') - except Exception: - hist.append('error') - finally: - hist.append('finally') - try: - hist.append((yield aspectlib.Return)) - except GeneratorExit: - hist.append('closed') - raise - else: - hist.append('consumed') - hist.append('bad-suffix') - - @aspect - def func(): - val = 99 - for _ in range(3): - print("YIELD", val + 1) - val = yield val + 1 - print("GOT", val) - raise StopIteration("the-return-value") - - gen = func() - data = [] - try: - for i in [None, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9]: - data.append(gen.send(i)) - except StopIteration: - data.append('done') - print(data) - assert data == [100, 1, 2, 'done'], hist - print(hist) - assert hist == ['before', 'the-return-value', 'after', 'finally', 'closed'] - - -def test_aspect_chain_on_generator(): - @aspectlib.Aspect - def foo(arg): - result = yield aspectlib.Proceed(arg + 1) - yield aspectlib.Return(result - 1) - - @foo - @foo - @foo - def func(a): - assert a == 3 - raise StopIteration(a) - yield - - gen = func(0) - result = pytest.raises(StopIteration, gen.__next__ if hasattr(gen, '__next__') else gen.next) - assert result.value.args == (0,) - - -def test_aspect_chain_on_generator_no_return_advice(): - @aspectlib.Aspect - def foo(arg): - yield aspectlib.Proceed(arg + 1) - - @foo - @foo - @foo - def func(a): - assert a == 3 - raise StopIteration(a) - yield - - gen = func(0) - if hasattr(gen, '__next__'): - result = pytest.raises(StopIteration, gen.__next__) - else: - result = pytest.raises(StopIteration, gen.next) - assert result.value.args == (3,) - - -def test_aspect_on_generator_raise_stopiteration(): - result = [] - - @aspectlib.Aspect - def aspect(): - val = yield aspectlib.Proceed - result.append(val) - - @aspect - def func(): - raise StopIteration('something') - yield - - assert list(func()) == [] - assert result == ['something'] - - -def test_aspect_on_generator_result_from_aspect(): - @aspectlib.Aspect - def aspect(): - yield aspectlib.Proceed - yield aspectlib.Return('result') - - @aspect - def func(): - yield 'something' - - gen = func() - try: - while 1: - next(gen) - except StopIteration as exc: - assert exc.args == ('result',) - else: - raise AssertionError("did not raise StopIteration") - - -def test_aspect_chain_on_generator_no_return(): - @aspectlib.Aspect - def foo(arg): - result = yield aspectlib.Proceed(arg + 1) - yield aspectlib.Return(result) - - @foo - @foo - @foo - def func(a): - assert a == 3 - yield - - gen = func(0) - if hasattr(gen, '__next__'): - assert gen.__next__() is None - result = pytest.raises(StopIteration, gen.__next__) - else: - assert gen.next() is None - result = pytest.raises(StopIteration, gen.next) - assert result.value.args == (None,) diff --git a/tests/test_aspectlib_py3.py b/tests/test_aspectlib_py3.py index db9864d..40c056b 100644 --- a/tests/test_aspectlib_py3.py +++ b/tests/test_aspectlib_py3.py @@ -1,5 +1,5 @@ # encoding: utf8 -from __future__ import print_function + import pytest diff --git a/tests/test_aspectlib_py37.py b/tests/test_aspectlib_py37.py index 43e340b..96b112b 100644 --- a/tests/test_aspectlib_py37.py +++ b/tests/test_aspectlib_py37.py @@ -1,15 +1,12 @@ # encoding: utf8 -from __future__ import print_function + import pytest import aspectlib -from aspectlib.utils import PY37plus from test_aspectlib_py3 import consume -pytestmark = pytest.mark.skipif(not PY37plus, reason="Tests only work with PEP-479") - def test_aspect_on_generator_result(): result = [] diff --git a/tests/test_aspectlib_test.py b/tests/test_aspectlib_test.py index 05e2c25..16c8e9c 100644 --- a/tests/test_aspectlib_test.py +++ b/tests/test_aspectlib_test.py @@ -1,9 +1,6 @@ -from __future__ import print_function - from pytest import raises from test_pkg1.test_pkg2 import test_mod -from aspectlib import PY2 from aspectlib.test import OrderedDict from aspectlib.test import Story from aspectlib.test import StoryResultWrapper @@ -13,10 +10,10 @@ from aspectlib.test import _Returns from aspectlib.test import mock from aspectlib.test import record -from aspectlib.utils import PY26 +from aspectlib.utils import PY310 from aspectlib.utils import repr_ex -pytest_plugins = 'pytester', +pytest_plugins = ('pytester',) def format_calls(calls): @@ -49,7 +46,7 @@ def test_record(): assert fun(3, b=4) == (3, 4) assert fun.calls == [ (None, (2, 3), {}), - (None, (3, ), {'b': 4}), + (None, (3,), {'b': 4}), ] @@ -60,7 +57,7 @@ def test_record_result(): assert fun(3, b=4) == (3, 4) assert fun.calls == [ (None, (2, 3), {}, (2, 3), None), - (None, (3, ), {'b': 4}, (3, 4), None), + (None, (3,), {'b': 4}, (3, 4), None), ] @@ -82,7 +79,7 @@ def test_record_result_callback(): assert fun(3, b=4) == (3, 4) assert calls == [ (None, 'test_aspectlib_test.nfun', (2, 3), {}, (2, 3), None), - (None, 'test_aspectlib_test.nfun', (3, ), {'b': 4}, (3, 4), None), + (None, 'test_aspectlib_test.nfun', (3,), {'b': 4}, (3, 4), None), ] @@ -106,7 +103,7 @@ def test_record_callback(): assert fun(3, b=4) == (3, 4) assert calls == [ (None, 'test_aspectlib_test.nfun', (2, 3), {}), - (None, 'test_aspectlib_test.nfun', (3, ), {'b': 4}), + (None, 'test_aspectlib_test.nfun', (3,), {'b': 4}), ] @@ -145,7 +142,7 @@ def test_record_as_context(): assert history.calls == [ (None, (2, 3), {}), - (None, (3, ), {'b': 4}), + (None, (3,), {'b': 4}), ] del history.calls[:] @@ -217,13 +214,23 @@ def test_story_empty_play_proxy(): assert test_mod.target() is None raises(TypeError, test_mod.target, 123) - assert format_calls(replay._actual) == format_calls(OrderedDict([ - ((None, 'test_pkg1.test_pkg2.test_mod.target', '', ''), _Returns("None")), - ((None, 'test_pkg1.test_pkg2.test_mod.target', '123', ''), _Raises(repr_ex(TypeError( - 'target() takes no arguments (1 given)' if PY2 else - 'target() takes 0 positional arguments but 1 was given', - )))) - ])) + assert format_calls(replay._actual) == format_calls( + OrderedDict( + [ + ((None, 'test_pkg1.test_pkg2.test_mod.target', '', ''), _Returns("None")), + ( + (None, 'test_pkg1.test_pkg2.test_mod.target', '123', ''), + _Raises( + repr_ex( + TypeError( + 'target() takes 0 positional arguments but 1 was given', + ) + ) + ), + ), + ] + ) + ) def test_story_empty_play_noproxy_class(): @@ -237,9 +244,7 @@ def test_story_empty_play_error_on_init(): with Story(test_mod).replay(strict=False) as replay: raises(ValueError, test_mod.Stuff, "error") print(replay._actual) - assert replay._actual == OrderedDict([ - ((None, 'test_pkg1.test_pkg2.test_mod.Stuff', "'error'", ''), _Raises('ValueError()')) - ]) + assert replay._actual == OrderedDict([((None, 'test_pkg1.test_pkg2.test_mod.Stuff', "'error'", ''), _Raises('ValueError()'))]) def test_story_half_play_noproxy_class(): @@ -303,36 +308,27 @@ def test_story_text_helpers(): test_mod.target(1) print(replay.missing) - assert replay.missing == """stuff_1.meth('b') == 'y' # returns + assert ( + replay.missing + == """stuff_1.meth('b') == 'y' # returns stuff_2 = test_pkg1.test_pkg2.test_mod.Stuff(2, 3) stuff_2.meth('c') == 'z' # returns test_pkg1.test_pkg2.test_mod.target(2) == 3 # returns """ + ) print(replay.unexpected) - assert replay.unexpected == """stuff_1.meth() == None # returns + assert ( + replay.unexpected + == """stuff_1.meth() == None # returns stuff_2 = test_pkg1.test_pkg2.test_mod.Stuff(4, 4) stuff_2.meth() == None # returns test_pkg1.test_pkg2.test_mod.func(5) == None # returns """ + ) print(replay.diff) - if PY26: - assert replay.diff == """--- expected """ """ -+++ actual """ """ -@@ -1,7 +1,7 @@ - stuff_1 = test_pkg1.test_pkg2.test_mod.Stuff(1, 2) - stuff_1.meth('a') == 'x' # returns --stuff_1.meth('b') == 'y' # returns --stuff_2 = test_pkg1.test_pkg2.test_mod.Stuff(2, 3) --stuff_2.meth('c') == 'z' # returns -+stuff_1.meth() == None # returns -+stuff_2 = test_pkg1.test_pkg2.test_mod.Stuff(4, 4) -+stuff_2.meth() == None # returns -+test_pkg1.test_pkg2.test_mod.func(5) == None # returns - test_pkg1.test_pkg2.test_mod.target(1) == 2 # returns --test_pkg1.test_pkg2.test_mod.target(2) == 3 # returns -""" - else: - assert replay.diff == """--- expected + assert ( + replay.diff + == """--- expected +++ actual @@ -1,7 +1,7 @@ stuff_1 = test_pkg1.test_pkg2.test_mod.Stuff(1, 2) @@ -347,6 +343,7 @@ def test_story_text_helpers(): test_pkg1.test_pkg2.test_mod.target(1) == 2 # returns -test_pkg1.test_pkg2.test_mod.target(2) == 3 # returns """ + ) def test_story_empty_play_proxy_class_missing_report(LineMatcher): @@ -367,29 +364,31 @@ def test_story_empty_play_proxy_class_missing_report(LineMatcher): obj.mix() obj.meth() obj.mix(10) - LineMatcher(replay.diff.splitlines()).fnmatch_lines([ - "--- expected", - "+++ actual", - "@@ -0,0 +1,18 @@", - "+stuff_1 = test_pkg1.test_pkg2.test_mod.Stuff(1, 2)", - "+stuff_1.mix(3, 4) == (1, 2, 3, 4) # returns", - "+stuff_1.mix('a', 'b') == (1, 2, 'a', 'b') # returns", - "+stuff_1.raises(123) ** ValueError((123,)*) # raises", - "+stuff_2 = test_pkg1.test_pkg2.test_mod.Stuff(0, 1)", - "+stuff_2.mix('a', 'b') == (0, 1, 'a', 'b') # returns", - "+stuff_2.mix(3, 4) == (0, 1, 3, 4) # returns", - "+test_pkg1.test_pkg2.test_mod.target() == None # returns", - "+test_pkg1.test_pkg2.test_mod.raises('badarg') ** ValueError(('badarg',)*) # raises", - "+stuff_2.raises(123) ** ValueError((123,)*) # raises", - "+that_long_stuf_1 = test_pkg1.test_pkg2.test_mod.ThatLONGStuf(1)", - "+that_long_stuf_1.mix(2) == (1, 2) # returns", - "+that_long_stuf_2 = test_pkg1.test_pkg2.test_mod.ThatLONGStuf(3)", - "+that_long_stuf_2.mix(4) == (3, 4) # returns", - "+that_long_stuf_3 = test_pkg1.test_pkg2.test_mod.ThatLONGStuf(2)", - "+that_long_stuf_3.mix() == (2,) # returns", - "+that_long_stuf_3.meth() == None # returns", - "+that_long_stuf_3.mix(10) == (2, 10) # returns", - ]) + LineMatcher(replay.diff.splitlines()).fnmatch_lines( + [ + "--- expected", + "+++ actual", + "@@ -0,0 +1,18 @@", + "+stuff_1 = test_pkg1.test_pkg2.test_mod.Stuff(1, 2)", + "+stuff_1.mix(3, 4) == (1, 2, 3, 4) # returns", + "+stuff_1.mix('a', 'b') == (1, 2, 'a', 'b') # returns", + "+stuff_1.raises(123) ** ValueError((123,)*) # raises", + "+stuff_2 = test_pkg1.test_pkg2.test_mod.Stuff(0, 1)", + "+stuff_2.mix('a', 'b') == (0, 1, 'a', 'b') # returns", + "+stuff_2.mix(3, 4) == (0, 1, 3, 4) # returns", + "+test_pkg1.test_pkg2.test_mod.target() == None # returns", + "+test_pkg1.test_pkg2.test_mod.raises('badarg') ** ValueError(('badarg',)*) # raises", + "+stuff_2.raises(123) ** ValueError((123,)*) # raises", + "+that_long_stuf_1 = test_pkg1.test_pkg2.test_mod.ThatLONGStuf(1)", + "+that_long_stuf_1.mix(2) == (1, 2) # returns", + "+that_long_stuf_2 = test_pkg1.test_pkg2.test_mod.ThatLONGStuf(3)", + "+that_long_stuf_2.mix(4) == (3, 4) # returns", + "+that_long_stuf_3 = test_pkg1.test_pkg2.test_mod.ThatLONGStuf(2)", + "+that_long_stuf_3.mix() == (2,) # returns", + "+that_long_stuf_3.meth() == None # returns", + "+that_long_stuf_3.mix(10) == (2, 10) # returns", + ] + ) def test_story_empty_play_proxy_class(): @@ -408,22 +407,42 @@ def test_story_empty_play_proxy_class(): raises(TypeError, obj.meth, 123) - assert format_calls(replay._actual) == format_calls(OrderedDict([ - ((None, 'test_pkg1.test_pkg2.test_mod.Stuff', "1, 2", ''), _Binds('stuff_1')), - (('stuff_1', 'mix', "3, 4", ''), _Returns("(1, 2, 3, 4)")), - (('stuff_1', 'mix', "'a', 'b'", ''), _Returns("(1, 2, 'a', 'b')")), - (('stuff_1', 'meth', "123", ''), _Raises(repr_ex(TypeError( - 'meth() takes exactly 1 argument (2 given)' if PY2 else - 'meth() takes 1 positional argument but 2 were given' - )))), - ((None, 'test_pkg1.test_pkg2.test_mod.Stuff', "0, 1", ''), _Binds('stuff_2')), - (('stuff_2', 'mix', "'a', 'b'", ''), _Returns("(0, 1, 'a', 'b')")), - (('stuff_2', 'mix', "3, 4", ''), _Returns("(0, 1, 3, 4)")), - (('stuff_2', 'meth', "123", ''), _Raises(repr_ex(TypeError( - 'meth() takes exactly 1 argument (2 given)' if PY2 else - 'meth() takes 1 positional argument but 2 were given' - )))) - ])) + assert format_calls(replay._actual) == format_calls( + OrderedDict( + [ + ((None, 'test_pkg1.test_pkg2.test_mod.Stuff', "1, 2", ''), _Binds('stuff_1')), + (('stuff_1', 'mix', "3, 4", ''), _Returns("(1, 2, 3, 4)")), + (('stuff_1', 'mix', "'a', 'b'", ''), _Returns("(1, 2, 'a', 'b')")), + ( + ('stuff_1', 'meth', "123", ''), + _Raises( + repr_ex( + TypeError( + 'Stuff.meth() takes 1 positional argument but 2 were given' + if PY310 + else 'meth() takes 1 positional argument but 2 were given' + ) + ) + ), + ), + ((None, 'test_pkg1.test_pkg2.test_mod.Stuff', "0, 1", ''), _Binds('stuff_2')), + (('stuff_2', 'mix', "'a', 'b'", ''), _Returns("(0, 1, 'a', 'b')")), + (('stuff_2', 'mix', "3, 4", ''), _Returns("(0, 1, 3, 4)")), + ( + ('stuff_2', 'meth', "123", ''), + _Raises( + repr_ex( + TypeError( + 'Stuff.meth() takes 1 positional argument but 2 were given' + if PY310 + else 'meth() takes 1 positional argument but 2 were given' + ) + ) + ), + ), + ] + ) + ) def test_story_half_play_proxy_class(): @@ -445,20 +464,40 @@ def test_story_half_play_proxy_class(): assert obj.mix(3, 4) == (0, 1, 3, 4) raises(TypeError, obj.meth, 123) - assert replay.unexpected == format_calls(OrderedDict([ - (('stuff_1', 'meth', '', ''), _Returns('None')), - (('stuff_1', 'meth', '123', ''), _Raises(repr_ex(TypeError( - 'meth() takes exactly 1 argument (2 given)' if PY2 else - 'meth() takes 1 positional argument but 2 were given' - )))), - ((None, 'test_pkg1.test_pkg2.test_mod.Stuff', '0, 1', ''), _Binds("stuff_2")), - (('stuff_2', 'mix', "'a', 'b'", ''), _Returns("(0, 1, 'a', 'b')")), - (('stuff_2', 'mix', '3, 4', ''), _Returns('(0, 1, 3, 4)')), - (('stuff_2', 'meth', '123', ''), _Raises(repr_ex(TypeError( - 'meth() takes exactly 1 argument (2 given)' if PY2 else - 'meth() takes 1 positional argument but 2 were given' - )))) - ])) + assert replay.unexpected == format_calls( + OrderedDict( + [ + (('stuff_1', 'meth', '', ''), _Returns('None')), + ( + ('stuff_1', 'meth', '123', ''), + _Raises( + repr_ex( + TypeError( + 'Stuff.meth() takes 1 positional argument but 2 were given' + if PY310 + else 'meth() takes 1 positional argument but 2 were given' + ) + ) + ), + ), + ((None, 'test_pkg1.test_pkg2.test_mod.Stuff', '0, 1', ''), _Binds("stuff_2")), + (('stuff_2', 'mix', "'a', 'b'", ''), _Returns("(0, 1, 'a', 'b')")), + (('stuff_2', 'mix', '3, 4', ''), _Returns('(0, 1, 3, 4)')), + ( + ('stuff_2', 'meth', '123', ''), + _Raises( + repr_ex( + TypeError( + 'Stuff.meth() takes 1 positional argument but 2 were given' + if PY310 + else 'meth() takes 1 positional argument but 2 were given' + ) + ) + ), + ), + ] + ) + ) def test_story_full_play_noproxy(): @@ -498,14 +537,23 @@ def test_story_full_play_proxy(): raises(ValueError, test_mod.target, 1234) raises(TypeError, test_mod.target, 'asdf') - assert replay.unexpected == format_calls(OrderedDict([ - ((None, 'test_pkg1.test_pkg2.test_mod.target', '', ''), _Returns("None")), - ((None, 'test_pkg1.test_pkg2.test_mod.target', "'asdf'", ''), _Raises(repr_ex(TypeError( - 'target() takes no arguments (1 given)' - if PY2 - else 'target() takes 0 positional arguments but 1 was given',) - ))) - ])) + assert replay.unexpected == format_calls( + OrderedDict( + [ + ((None, 'test_pkg1.test_pkg2.test_mod.target', '', ''), _Returns("None")), + ( + (None, 'test_pkg1.test_pkg2.test_mod.target', "'asdf'", ''), + _Raises( + repr_ex( + TypeError( + 'target() takes 0 positional arguments but 1 was given', + ) + ) + ), + ), + ] + ) + ) def test_story_result_wrapper(): @@ -521,8 +569,8 @@ def test_story_result_wrapper(): def test_story_result_wrapper_bad_exception(): x = StoryResultWrapper(lambda *a: None) - raises(RuntimeError, lambda: x ** 1) - x ** Exception + raises(RuntimeError, lambda: x**1) + x**Exception x ** Exception('boom!') @@ -538,7 +586,7 @@ def test_story_create(): # from pprint import pprint as print # print (dict(story._calls)) assert dict(story._calls) == { - (None, 'test_pkg1.test_pkg2.test_mod.Stuff', "'stuff'", ''): _Binds('stuff_1'), + (None, 'test_pkg1.test_pkg2.test_mod.Stuff', "'stuff'", ''): _Binds('stuff_1'), ('stuff_1', 'meth', "'other', 1, 2", ''): _Returns("123"), ('stuff_1', 'mix', "'other'", ''): _Returns("'mixymix'"), (None, 'test_pkg1.test_pkg2.test_mod.target', '', ''): _Raises("Exception"), diff --git a/tests/test_integrations.py b/tests/test_integrations.py index e4dc004..08f6e13 100644 --- a/tests/test_integrations.py +++ b/tests/test_integrations.py @@ -1,15 +1,18 @@ -from __future__ import print_function - +import asyncio import os import re import socket import warnings +from datetime import timedelta import pytest from process_tests import dump_on_error from process_tests import wait_for_strings +from tornado import gen +from tornado import ioloop import aspectlib +from aspectlib import debug from aspectlib.test import mock from aspectlib.test import record from aspectlib.utils import PYPY @@ -60,11 +63,7 @@ def test_fork(): def test_socket(target=socket.socket): buf = StringIO() - with aspectlib.weave(target, aspectlib.debug.log( - print_to=buf, - stacktrace=4, - module=False - ), lazy=True): + with aspectlib.weave(target, aspectlib.debug.log(print_to=buf, stacktrace=4, module=False), lazy=True): s = socket.socket() try: s.connect(('127.0.0.1', 1)) @@ -106,12 +105,7 @@ def test_socket_meth_as_string_target(): def test_socket_all_methods(): buf = StringIO() - with aspectlib.weave( - socket.socket, - aspectlib.debug.log(print_to=buf, stacktrace=False), - lazy=True, - methods=aspectlib.ALL_METHODS - ): + with aspectlib.weave(socket.socket, aspectlib.debug.log(print_to=buf, stacktrace=False), lazy=True, methods=aspectlib.ALL_METHODS): socket.socket() assert "}.__init__ => None" in buf.getvalue() @@ -128,8 +122,7 @@ def test_realsocket_makefile(): if pid: with aspectlib.weave( - ['socket._fileobject' if aspectlib.PY2 else 'socket.SocketIO'] + - (['socket.socket', 'socket._realsocket'] if aspectlib.PY2 else ['socket.socket']), + ['socket.SocketIO', 'socket.socket'], aspectlib.debug.log(print_to=buf, stacktrace=False), lazy=True, methods=aspectlib.ALL_METHODS, @@ -137,16 +130,14 @@ def test_realsocket_makefile(): s = socket.socket() s.settimeout(1) s.connect(p.getsockname()) - if aspectlib.PY3: - fh = s.makefile('rwb', buffering=0) - else: - fh = s.makefile(bufsize=0) + fh = s.makefile('rwb', buffering=0) fh.write(b"STUFF\n") fh.readline() with dump_on_error(buf.getvalue): wait_for_strings( - buf.getvalue, 0, + buf.getvalue, + 0, "}.connect", "}.makefile", "}.write(", @@ -160,10 +151,7 @@ def test_realsocket_makefile(): try: c, _ = p.accept() c.settimeout(1) - if aspectlib.PY3: - f = c.makefile('rw', buffering=1) - else: - f = c.makefile(bufsize=1) + f = c.makefile('rw', buffering=1) while f.readline(): f.write('-\n') finally: @@ -177,7 +165,43 @@ def test_weave_os_module(): os.getenv('BUBU', 'bubu') os.walk('.') - assert calls == [ - (None, 'os.getenv', ('BUBU', 'bubu'), {}), - (None, 'os.walk', ('.',), {}) - ] + assert calls == [(None, 'os.getenv', ('BUBU', 'bubu'), {}), (None, 'os.walk', ('.',), {})] + + +def test_decorate_asyncio_coroutine(): + buf = StringIO() + + @debug.log(print_to=buf, module=False, stacktrace=2, result_repr=repr) + async def coro(): + await asyncio.sleep(0.01) + return "result" + + loop = asyncio.new_event_loop() + loop.run_until_complete(coro()) + output = buf.getvalue() + print(output) + assert 'coro => %r' % 'result' in output + + +def test_decorate_tornado_coroutine(): + buf = StringIO() + + @gen.coroutine + @debug.log(print_to=buf, module=False, stacktrace=2, result_repr=repr) + def coro(): + if hasattr(gen, 'Task'): + yield gen.Task(loop.add_timeout, timedelta(microseconds=10)) + else: + yield gen.sleep(0.01) + return "result" + + asyncio_loop = asyncio.new_event_loop() + try: + get_event_loop = asyncio.get_event_loop + asyncio.get_event_loop = lambda: asyncio_loop + loop = ioloop.IOLoop.current() + loop.run_sync(coro) + finally: + asyncio.get_event_loop = get_event_loop + output = buf.getvalue() + assert 'coro => %r' % 'result' in output diff --git a/tests/test_integrations_py2.py b/tests/test_integrations_py2.py deleted file mode 100644 index fc05188..0000000 --- a/tests/test_integrations_py2.py +++ /dev/null @@ -1,64 +0,0 @@ -try: - from StringIO import StringIO -except ImportError: - from io import StringIO -from datetime import timedelta - -import pytest -from tornado import gen -from tornado import ioloop - -from aspectlib import debug -from aspectlib.test import Story - -asyncio = pytest.importorskip("trollius") - -try: - import MySQLdb -except ImportError: - MySQLdb = None - - -def test_decorate_asyncio_coroutine(): - buf = StringIO() - - @asyncio.coroutine - @debug.log(print_to=buf, module=False, stacktrace=2, result_repr=repr) - def coro(): - yield asyncio.From(asyncio.sleep(0.01)) - raise StopIteration("result") - - loop = asyncio.get_event_loop() - loop.run_until_complete(coro()) - output = buf.getvalue() - assert 'coro => %r' % 'result' in output - - -def test_decorate_tornado_coroutine(): - buf = StringIO() - - @gen.coroutine - @debug.log(print_to=buf, module=False, stacktrace=2, result_repr=repr) - def coro(): - yield gen.Task(loop.add_timeout, timedelta(microseconds=10)) - raise StopIteration("result") - - loop = ioloop.IOLoop.current() - loop.run_sync(coro) - output = buf.getvalue() - assert 'coro => %r' % 'result' in output - - -@pytest.mark.skipif(not MySQLdb, reason="No MySQLdb installed") -def test_mysql(): - with Story(['MySQLdb.cursors.BaseCursor', 'MySQLdb.connections.Connection']) as story: - pass - rows = [] - with story.replay(strict=False) as replay: - import MySQLdb - con = MySQLdb.connect('localhost', 'root', '') - con.select_db('mysql') - cursor = con.cursor() - cursor.execute('show tables') - rows.extend(cursor.fetchall()) - assert '== (%s)' % ', '.join(repr(row) for row in rows) in replay.unexpected diff --git a/tests/test_integrations_py3.py b/tests/test_integrations_py3.py deleted file mode 100644 index 596589d..0000000 --- a/tests/test_integrations_py3.py +++ /dev/null @@ -1,48 +0,0 @@ -import asyncio -from datetime import timedelta - -import pytest -from tornado import gen -from tornado import ioloop - -from aspectlib import PY37plus -from aspectlib import debug - -try: - from StringIO import StringIO -except ImportError: - from io import StringIO - - -@pytest.mark.skipif(PY37plus, reason="Test is incompatible with PEP-479") -def test_decorate_asyncio_coroutine(): - buf = StringIO() - - @asyncio.coroutine - @debug.log(print_to=buf, module=False, stacktrace=2, result_repr=repr) - def coro(): - yield from asyncio.sleep(0.01) - return "result" - - loop = asyncio.get_event_loop() - loop.run_until_complete(coro()) - output = buf.getvalue() - assert 'coro => %r' % 'result' in output - - -def test_decorate_tornado_coroutine(): - buf = StringIO() - - @gen.coroutine - @debug.log(print_to=buf, module=False, stacktrace=2, result_repr=repr) - def coro(): - if hasattr(gen, 'Task'): - yield gen.Task(loop.add_timeout, timedelta(microseconds=10)) - else: - yield gen.sleep(0.01) - return "result" - - loop = ioloop.IOLoop.current() - loop.run_sync(coro) - output = buf.getvalue() - assert 'coro => %r' % 'result' in output diff --git a/tests/test_integrations_py37.py b/tests/test_integrations_py37.py deleted file mode 100644 index 2e77762..0000000 --- a/tests/test_integrations_py37.py +++ /dev/null @@ -1,23 +0,0 @@ -import asyncio - -from aspectlib import debug - -try: - from StringIO import StringIO -except ImportError: - from io import StringIO - - -def test_decorate_asyncio_coroutine(): - buf = StringIO() - - @debug.log(print_to=buf, module=False, stacktrace=2, result_repr=repr) - async def coro(): - await asyncio.sleep(0.01) - return "result" - - loop = asyncio.get_event_loop() - loop.run_until_complete(coro()) - output = buf.getvalue() - print(output) - assert 'coro => %r' % 'result' in output diff --git a/tox.ini b/tox.ini index 1bd1d1c..0b23773 100644 --- a/tox.ini +++ b/tox.ini @@ -1,68 +1,54 @@ +[testenv:bootstrap] +deps = + jinja2 + tox +skip_install = true +commands = + python ci/bootstrap.py --no-env +passenv = + * +; a generative tox configuration, see: https://tox.readthedocs.io/en/latest/config.html#generative-envlist + [tox] envlist = clean, check, docs, - py27-cover, - py27-cover-debug, - py27-nocov, - py27-nocov-debug, - py36-cover, - py36-cover-debug, - py36-nocov, - py36-nocov-debug, - py37-cover, - py37-cover-debug, - py37-nocov, - py37-nocov-debug, - py38-cover, - py38-cover-debug, - py38-nocov, - py38-nocov-debug, - py39-cover, - py39-cover-debug, - py39-nocov, - py39-nocov-debug, - pypy-cover, - pypy-cover-debug, - pypy-nocov, - pypy-nocov-debug, - pypy3-cover, - pypy3-cover-debug, - pypy3-nocov, - pypy3-nocov-debug, + {py37,py38,py39,py310,pypy37,pypy38,pypy39}-{cover,nocov}-{release,debug}, report +ignore_basepython_conflict = true [testenv] basepython = + pypy37: {env:TOXPYTHON:pypy3.7} + pypy38: {env:TOXPYTHON:pypy3.8} + pypy39: {env:TOXPYTHON:pypy3.9} + py37: {env:TOXPYTHON:python3.7} + py38: {env:TOXPYTHON:python3.8} + py39: {env:TOXPYTHON:python3.9} + py310: {env:TOXPYTHON:python3.10} {bootstrap,clean,check,report,docs,codecov,coveralls}: {env:TOXPYTHON:python3} setenv = PYTHONPATH={toxinidir}/tests PYTHONUNBUFFERED=yes + debug: ASPECTLIB_DEBUG=yes passenv = * +usedevelop = + cover: true + nocov: false deps = hunter mock nose process-tests pytest - pytest-catchlog - pytest-clarity - pytest-cov - pytest-travis-fold six tornado + cover: pytest-cov commands = - {posargs:pytest --cov --cov-report=term-missing -vv --ignore=src} - -[testenv:bootstrap] -deps = - jinja2 - matrix -skip_install = true -commands = - python ci/bootstrap.py --no-env + nocov: {posargs:pytest -vv --ignore=src} + cover: {posargs:pytest --cov --cov-report=term-missing -vv} [testenv:check] deps = @@ -79,7 +65,6 @@ commands = flake8 isort --verbose --check-only --diff --filter-files . - [testenv:docs] usedevelop = true deps = @@ -96,8 +81,6 @@ skip_install = true commands = coveralls [] - - [testenv:codecov] deps = codecov @@ -106,7 +89,8 @@ commands = codecov [] [testenv:report] -deps = coverage +deps = + coverage skip_install = true commands = coverage report @@ -115,244 +99,5 @@ commands = [testenv:clean] commands = coverage erase skip_install = true -deps = coverage - -[testenv:py27-cover] -basepython = {env:TOXPYTHON:python2.7} -setenv = - {[testenv]setenv} -usedevelop = true -commands = - {posargs:pytest --cov --cov-report=term-missing -vv} -deps = - {[testenv]deps} - pytest-cov - trollius - -[testenv:py27-cover-debug] -basepython = {env:TOXPYTHON:python2.7} -setenv = - {[testenv]setenv} - ASPECTLIB_DEBUG=yes -usedevelop = true -commands = - {posargs:pytest --cov --cov-report=term-missing -vv} -deps = - {[testenv]deps} - pytest-cov - trollius - -[testenv:py27-nocov] -basepython = {env:TOXPYTHON:python2.7} -deps = - {[testenv]deps} - trollius - -[testenv:py27-nocov-debug] -basepython = {env:TOXPYTHON:python2.7} -setenv = - {[testenv]setenv} - ASPECTLIB_DEBUG=yes -deps = - {[testenv]deps} - trollius - -[testenv:py36-cover] -basepython = {env:TOXPYTHON:python3.6} -setenv = - {[testenv]setenv} -usedevelop = true -commands = - {posargs:pytest --cov --cov-report=term-missing -vv} -deps = - {[testenv]deps} - pytest-cov - -[testenv:py36-cover-debug] -basepython = {env:TOXPYTHON:python3.6} -setenv = - {[testenv]setenv} - ASPECTLIB_DEBUG=yes -usedevelop = true -commands = - {posargs:pytest --cov --cov-report=term-missing -vv} -deps = - {[testenv]deps} - pytest-cov - -[testenv:py36-nocov] -basepython = {env:TOXPYTHON:python3.6} - -[testenv:py36-nocov-debug] -basepython = {env:TOXPYTHON:python3.6} -setenv = - {[testenv]setenv} - ASPECTLIB_DEBUG=yes - -[testenv:py37-cover] -basepython = {env:TOXPYTHON:python3.7} -setenv = - {[testenv]setenv} -usedevelop = true -commands = - {posargs:pytest --cov --cov-report=term-missing -vv} -deps = - {[testenv]deps} - pytest-cov - -[testenv:py37-cover-debug] -basepython = {env:TOXPYTHON:python3.7} -setenv = - {[testenv]setenv} - ASPECTLIB_DEBUG=yes -usedevelop = true -commands = - {posargs:pytest --cov --cov-report=term-missing -vv} deps = - {[testenv]deps} - pytest-cov - -[testenv:py37-nocov] -basepython = {env:TOXPYTHON:python3.7} - -[testenv:py37-nocov-debug] -basepython = {env:TOXPYTHON:python3.7} -setenv = - {[testenv]setenv} - ASPECTLIB_DEBUG=yes - -[testenv:py38-cover] -basepython = {env:TOXPYTHON:python3.8} -setenv = - {[testenv]setenv} -usedevelop = true -commands = - {posargs:pytest --cov --cov-report=term-missing -vv} -deps = - {[testenv]deps} - pytest-cov - -[testenv:py38-cover-debug] -basepython = {env:TOXPYTHON:python3.8} -setenv = - {[testenv]setenv} - ASPECTLIB_DEBUG=yes -usedevelop = true -commands = - {posargs:pytest --cov --cov-report=term-missing -vv} -deps = - {[testenv]deps} - pytest-cov - -[testenv:py38-nocov] -basepython = {env:TOXPYTHON:python3.8} - -[testenv:py38-nocov-debug] -basepython = {env:TOXPYTHON:python3.8} -setenv = - {[testenv]setenv} - ASPECTLIB_DEBUG=yes - -[testenv:py39-cover] -basepython = {env:TOXPYTHON:python3.9} -setenv = - {[testenv]setenv} -usedevelop = true -commands = - {posargs:pytest --cov --cov-report=term-missing -vv} -deps = - {[testenv]deps} - pytest-cov - -[testenv:py39-cover-debug] -basepython = {env:TOXPYTHON:python3.9} -setenv = - {[testenv]setenv} - ASPECTLIB_DEBUG=yes -usedevelop = true -commands = - {posargs:pytest --cov --cov-report=term-missing -vv} -deps = - {[testenv]deps} - pytest-cov - -[testenv:py39-nocov] -basepython = {env:TOXPYTHON:python3.9} - -[testenv:py39-nocov-debug] -basepython = {env:TOXPYTHON:python3.9} -setenv = - {[testenv]setenv} - ASPECTLIB_DEBUG=yes - -[testenv:pypy-cover] -basepython = {env:TOXPYTHON:pypy} -setenv = - {[testenv]setenv} -usedevelop = true -commands = - {posargs:pytest --cov --cov-report=term-missing -vv} -deps = - {[testenv]deps} - pytest-cov - trollius - -[testenv:pypy-cover-debug] -basepython = {env:TOXPYTHON:pypy} -setenv = - {[testenv]setenv} - ASPECTLIB_DEBUG=yes -usedevelop = true -commands = - {posargs:pytest --cov --cov-report=term-missing -vv} -deps = - {[testenv]deps} - pytest-cov - trollius - -[testenv:pypy-nocov] -basepython = {env:TOXPYTHON:pypy} -deps = - {[testenv]deps} - trollius - -[testenv:pypy-nocov-debug] -basepython = {env:TOXPYTHON:pypy} -setenv = - {[testenv]setenv} - ASPECTLIB_DEBUG=yes -deps = - {[testenv]deps} - trollius - -[testenv:pypy3-cover] -basepython = {env:TOXPYTHON:pypy3} -setenv = - {[testenv]setenv} -usedevelop = true -commands = - {posargs:pytest --cov --cov-report=term-missing -vv} -deps = - {[testenv]deps} - pytest-cov - -[testenv:pypy3-cover-debug] -basepython = {env:TOXPYTHON:pypy3} -setenv = - {[testenv]setenv} - ASPECTLIB_DEBUG=yes -usedevelop = true -commands = - {posargs:pytest --cov --cov-report=term-missing -vv} -deps = - {[testenv]deps} - pytest-cov - -[testenv:pypy3-nocov] -basepython = {env:TOXPYTHON:pypy3} - -[testenv:pypy3-nocov-debug] -basepython = {env:TOXPYTHON:pypy3} -setenv = - {[testenv]setenv} - ASPECTLIB_DEBUG=yes + coverage From 3153065a20d83a4c3591cc119bc886c8544faa27 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ionel=20Cristian=20M=C4=83rie=C8=99?= Date: Thu, 20 Oct 2022 19:03:02 +0300 Subject: [PATCH 168/179] Remove some dead code and remove those runtime imports for what was py3 support. --- ci/bootstrap.py | 2 - setup.py | 2 +- src/aspectlib/__init__.py | 80 +++++++++++++++++++++++++-- src/aspectlib/py35support.py | 104 ----------------------------------- src/aspectlib/py3support.py | 56 ------------------- 5 files changed, 77 insertions(+), 167 deletions(-) delete mode 100644 src/aspectlib/py35support.py delete mode 100644 src/aspectlib/py3support.py diff --git a/ci/bootstrap.py b/ci/bootstrap.py index cc5ce2f..bf9bbd6 100755 --- a/ci/bootstrap.py +++ b/ci/bootstrap.py @@ -1,7 +1,5 @@ #!/usr/bin/env python # -*- coding: utf-8 -*- - - import os import subprocess import sys diff --git a/setup.py b/setup.py index 053f6b6..46606a7 100755 --- a/setup.py +++ b/setup.py @@ -86,7 +86,7 @@ def read(*names, **kwargs): 'mocking', 'logger', ], - python_requires='>=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*, !=3.5.*', + python_requires='>=3.7', install_requires=['fields'], extras_require={}, entry_points={ diff --git a/src/aspectlib/__init__.py b/src/aspectlib/__init__.py index a62ef18..396ec91 100644 --- a/src/aspectlib/__init__.py +++ b/src/aspectlib/__init__.py @@ -173,13 +173,85 @@ def __init__(self, advising_function, bind=False): def __call__(self, cutpoint_function): if isasyncfunction is not None and isasyncfunction(cutpoint_function): - from aspectlib.py35support import decorate_advising_asyncgenerator_py35 + assert isasyncgenfunction(cutpoint_function) or iscoroutinefunction(cutpoint_function) - return decorate_advising_asyncgenerator_py35(self.advising_function, cutpoint_function, self.bind) + async def advising_asyncgenerator_wrapper_py35(*args, **kwargs): + if self.bind: + advisor = self.advising_function(cutpoint_function, *args, **kwargs) + else: + advisor = self.advising_function(*args, **kwargs) + if not isgenerator(advisor): + raise ExpectedGenerator("advising_function %s did not return a generator." % self.advising_function) + try: + advice = next(advisor) + while True: + logdebug('Got advice %r from %s', advice, self.advising_function) + if advice is Proceed or advice is None or isinstance(advice, Proceed): + if isinstance(advice, Proceed): + args = advice.args + kwargs = advice.kwargs + gen = cutpoint_function(*args, **kwargs) + try: + result = await gen + except BaseException: + advice = advisor.throw(*sys.exc_info()) + else: + try: + advice = advisor.send(result) + except StopIteration: + return result + finally: + gen.close() + elif advice is Return: + return + elif isinstance(advice, Return): + return advice.value + else: + raise UnacceptableAdvice("Unknown advice %s" % advice) + finally: + advisor.close() + + return mimic(advising_asyncgenerator_wrapper_py35, cutpoint_function) elif isgeneratorfunction(cutpoint_function): - from aspectlib.py35support import decorate_advising_generator_py35 + assert isgeneratorfunction(cutpoint_function) + + def advising_generator_wrapper_py35(*args, **kwargs): + if self.bind: + advisor = self.advising_function(cutpoint_function, *args, **kwargs) + else: + advisor = self.advising_function(*args, **kwargs) + if not isgenerator(advisor): + raise ExpectedGenerator("advising_function %s did not return a generator." % self.advising_function) + try: + advice = next(advisor) + while True: + logdebug('Got advice %r from %s', advice, self.advising_function) + if advice is Proceed or advice is None or isinstance(advice, Proceed): + if isinstance(advice, Proceed): + args = advice.args + kwargs = advice.kwargs + gen = cutpoint_function(*args, **kwargs) + try: + result = yield from gen + except BaseException: + advice = advisor.throw(*sys.exc_info()) + else: + try: + advice = advisor.send(result) + except StopIteration: + return result + finally: + gen.close() + elif advice is Return: + return + elif isinstance(advice, Return): + return advice.value + else: + raise UnacceptableAdvice("Unknown advice %s" % advice) + finally: + advisor.close() - return decorate_advising_generator_py35(self.advising_function, cutpoint_function, self.bind) + return mimic(advising_generator_wrapper_py35, cutpoint_function) else: def advising_function_wrapper(*args, **kwargs): diff --git a/src/aspectlib/py35support.py b/src/aspectlib/py35support.py deleted file mode 100644 index 22f8b85..0000000 --- a/src/aspectlib/py35support.py +++ /dev/null @@ -1,104 +0,0 @@ -import sys -from inspect import iscoroutinefunction -from inspect import isgenerator -from inspect import isgeneratorfunction -from logging import getLogger - -from aspectlib import ExpectedGenerator -from aspectlib import Proceed -from aspectlib import Return -from aspectlib import UnacceptableAdvice -from aspectlib import mimic -from aspectlib.utils import logf - -try: - from inspect import isasyncgenfunction -except ImportError: - isasyncgenfunction = iscoroutinefunction - -logger = getLogger(__name__) -logdebug = logf(logger.debug) - - -def decorate_advising_asyncgenerator_py35(advising_function, cutpoint_function, bind): - assert isasyncgenfunction(cutpoint_function) or iscoroutinefunction(cutpoint_function) - - async def advising_asyncgenerator_wrapper_py35(*args, **kwargs): - if bind: - advisor = advising_function(cutpoint_function, *args, **kwargs) - else: - advisor = advising_function(*args, **kwargs) - if not isgenerator(advisor): - raise ExpectedGenerator("advising_function %s did not return a generator." % advising_function) - try: - advice = next(advisor) - while True: - logdebug('Got advice %r from %s', advice, advising_function) - if advice is Proceed or advice is None or isinstance(advice, Proceed): - if isinstance(advice, Proceed): - args = advice.args - kwargs = advice.kwargs - gen = cutpoint_function(*args, **kwargs) - try: - result = await gen - except BaseException: - advice = advisor.throw(*sys.exc_info()) - else: - try: - advice = advisor.send(result) - except StopIteration: - return result - finally: - gen.close() - elif advice is Return: - return - elif isinstance(advice, Return): - return advice.value - else: - raise UnacceptableAdvice("Unknown advice %s" % advice) - finally: - advisor.close() - - return mimic(advising_asyncgenerator_wrapper_py35, cutpoint_function) - - -def decorate_advising_generator_py35(advising_function, cutpoint_function, bind): - assert isgeneratorfunction(cutpoint_function) - - def advising_generator_wrapper_py35(*args, **kwargs): - if bind: - advisor = advising_function(cutpoint_function, *args, **kwargs) - else: - advisor = advising_function(*args, **kwargs) - if not isgenerator(advisor): - raise ExpectedGenerator("advising_function %s did not return a generator." % advising_function) - try: - advice = next(advisor) - while True: - logdebug('Got advice %r from %s', advice, advising_function) - if advice is Proceed or advice is None or isinstance(advice, Proceed): - if isinstance(advice, Proceed): - args = advice.args - kwargs = advice.kwargs - gen = cutpoint_function(*args, **kwargs) - try: - result = yield from gen - except BaseException: - advice = advisor.throw(*sys.exc_info()) - else: - try: - advice = advisor.send(result) - except StopIteration: - return result - finally: - gen.close() - elif advice is Return: - return - elif isinstance(advice, Return): - return advice.value - else: - raise UnacceptableAdvice("Unknown advice %s" % advice) - finally: - advisor.close() - - return mimic(advising_generator_wrapper_py35, cutpoint_function) diff --git a/src/aspectlib/py3support.py b/src/aspectlib/py3support.py deleted file mode 100644 index beaee11..0000000 --- a/src/aspectlib/py3support.py +++ /dev/null @@ -1,56 +0,0 @@ -import sys -from inspect import isgenerator -from inspect import isgeneratorfunction -from logging import getLogger - -from aspectlib import ExpectedGenerator -from aspectlib import Proceed -from aspectlib import Return -from aspectlib import UnacceptableAdvice -from aspectlib import mimic -from aspectlib.utils import logf - -logger = getLogger(__name__) -logdebug = logf(logger.debug) - - -def decorate_advising_generator_py3(advising_function, cutpoint_function, bind): - assert isgeneratorfunction(cutpoint_function) - - def advising_generator_wrapper_py3(*args, **kwargs): - if bind: - advisor = advising_function(cutpoint_function, *args, **kwargs) - else: - advisor = advising_function(*args, **kwargs) - if not isgenerator(advisor): - raise ExpectedGenerator("advising_function %s did not return a generator." % advising_function) - try: - advice = next(advisor) - while True: - logdebug('Got advice %r from %s', advice, advising_function) - if advice is Proceed or advice is None or isinstance(advice, Proceed): - if isinstance(advice, Proceed): - args = advice.args - kwargs = advice.kwargs - gen = cutpoint_function(*args, **kwargs) - try: - result = yield from gen - except BaseException: - advice = advisor.throw(*sys.exc_info()) - else: - try: - advice = advisor.send(result) - except StopIteration: - return result - finally: - gen.close() - elif advice is Return: - return - elif isinstance(advice, Return): - raise StopIteration(advice.value) - else: - raise UnacceptableAdvice("Unknown advice %s" % advice) - finally: - advisor.close() - - return mimic(advising_generator_wrapper_py3, cutpoint_function) From 5e6bd6a61c51353295a94c87b94ac8a0f8413b72 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ionel=20Cristian=20M=C4=83rie=C8=99?= Date: Thu, 20 Oct 2022 19:06:48 +0300 Subject: [PATCH 169/179] Update changelog. --- CHANGELOG.rst | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/CHANGELOG.rst b/CHANGELOG.rst index c4de5e2..dc0fe74 100644 --- a/CHANGELOG.rst +++ b/CHANGELOG.rst @@ -2,6 +2,13 @@ Changelog ========= +2.0.0 (2022-10-20) +------------------ + +* Drop support for legacy Pythons (2.7, 3.6 or older). +* Remove Travis/Appveyor CI and switch to GitHub Actions. +* Added support for Tornado 6 (in the test suite). + 1.5.2 (2020-11-15) ------------------ From 5ff96b4b152373a4ba47fe7635d1fb7d6415404b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ionel=20Cristian=20M=C4=83rie=C8=99?= Date: Thu, 20 Oct 2022 19:12:54 +0300 Subject: [PATCH 170/179] Move file in the right place. --- conftest.py => tests/conftest.py | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename conftest.py => tests/conftest.py (100%) diff --git a/conftest.py b/tests/conftest.py similarity index 100% rename from conftest.py rename to tests/conftest.py From 01c8d9c088988a3d920d47538c858816da339880 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ionel=20Cristian=20M=C4=83rie=C8=99?= Date: Thu, 20 Oct 2022 19:13:01 +0300 Subject: [PATCH 171/179] Fix formatting. --- CHANGELOG.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CHANGELOG.rst b/CHANGELOG.rst index dc0fe74..cce57be 100644 --- a/CHANGELOG.rst +++ b/CHANGELOG.rst @@ -191,7 +191,7 @@ Changelog 0.3.1 (2014-03-05) ------------------ -* ??? +* `???` 0.3.0 (2014-03-05) ------------------ From d5122f86c20e95b27bee3a1a38e00d628043cc9b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ionel=20Cristian=20M=C4=83rie=C8=99?= Date: Thu, 20 Oct 2022 19:22:58 +0300 Subject: [PATCH 172/179] =?UTF-8?q?Bump=20version:=201.5.2=20=E2=86=92=202?= =?UTF-8?q?.0.0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .bumpversion.cfg | 2 +- README.rst | 4 ++-- docs/conf.py | 2 +- setup.py | 2 +- src/aspectlib/__init__.py | 2 +- 5 files changed, 6 insertions(+), 6 deletions(-) diff --git a/.bumpversion.cfg b/.bumpversion.cfg index 8f991ed..ed83588 100644 --- a/.bumpversion.cfg +++ b/.bumpversion.cfg @@ -1,5 +1,5 @@ [bumpversion] -current_version = 1.5.2 +current_version = 2.0.0 commit = True tag = True diff --git a/README.rst b/README.rst index d89249c..a1bbfa2 100644 --- a/README.rst +++ b/README.rst @@ -51,9 +51,9 @@ Overview :alt: Supported implementations :target: https://pypi.org/project/aspectlib -.. |commits-since| image:: https://img.shields.io/github/commits-since/ionelmc/python-aspectlib/v1.5.2.svg +.. |commits-since| image:: https://img.shields.io/github/commits-since/ionelmc/python-aspectlib/v2.0.0.svg :alt: Commits since latest release - :target: https://github.com/ionelmc/python-aspectlib/compare/v1.5.2...main + :target: https://github.com/ionelmc/python-aspectlib/compare/v2.0.0...main diff --git a/docs/conf.py b/docs/conf.py index cd0320c..4055513 100644 --- a/docs/conf.py +++ b/docs/conf.py @@ -20,7 +20,7 @@ year = '2014-2022' author = 'Ionel Cristian Mărieș' copyright = '{0}, {1}'.format(year, author) -version = release = '1.5.2' +version = release = '2.0.0' pygments_style = 'trac' templates_path = ['.'] diff --git a/setup.py b/setup.py index 46606a7..0fd4847 100755 --- a/setup.py +++ b/setup.py @@ -20,7 +20,7 @@ def read(*names, **kwargs): setup( name='aspectlib', - version='1.5.2', + version='2.0.0', license='BSD-2-Clause', description='``aspectlib`` is an aspect-oriented programming, monkey-patch and decorators library. It is useful when changing', long_description='{}\n{}'.format( diff --git a/src/aspectlib/__init__.py b/src/aspectlib/__init__.py index 396ec91..f57cbbf 100644 --- a/src/aspectlib/__init__.py +++ b/src/aspectlib/__init__.py @@ -51,7 +51,7 @@ def isasyncfunction(obj): isasyncfunction = None __all__ = 'weave', 'Aspect', 'Proceed', 'Return', 'ALL_METHODS', 'NORMAL_METHODS', 'ABSOLUTELY_ALL_METHODS' -__version__ = '1.5.2' +__version__ = '2.0.0' logger = getLogger(__name__) logdebug = logf(logger.debug) From 14ea6dc1c7cc8da35c0baef5e03ae9d4324aacec Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ionel=20Cristian=20M=C4=83rie=C8=99?= Date: Mon, 12 Aug 2024 17:53:03 +0300 Subject: [PATCH 173/179] Up skel, add py 3.11 and 3.12 and drop 3.7. --- .bumpversion.cfg | 4 + .cookiecutterrc | 26 +--- .github/workflows/github-actions.yml | 145 ++++++++++++------ .gitignore | 58 +++---- .pre-commit-config.yaml | 29 ++-- .readthedocs.yml | 4 + CONTRIBUTING.rst | 4 +- LICENSE | 2 +- README.rst | 18 +-- ci/bootstrap.py | 74 ++++----- ci/requirements.txt | 1 + .../.github/workflows/github-actions.yml | 31 ++-- docs/conf.py | 22 +-- docs/index.rst | 8 +- docs/readme.rst | 1 + docs/reference/aspectlib.rst | 4 +- docs/requirements.txt | 2 +- pyproject.toml | 52 ++++++- setup.cfg | 11 -- setup.py | 28 ++-- tox.ini | 37 ++--- 21 files changed, 312 insertions(+), 249 deletions(-) create mode 100644 docs/readme.rst delete mode 100644 setup.cfg diff --git a/.bumpversion.cfg b/.bumpversion.cfg index ed83588..dc0a507 100644 --- a/.bumpversion.cfg +++ b/.bumpversion.cfg @@ -22,3 +22,7 @@ replace = version = release = '{new_version}' [bumpversion:file:src/aspectlib/__init__.py] search = __version__ = '{current_version}' replace = __version__ = '{new_version}' + +[bumpversion:file:.cookiecutterrc] +search = version: {current_version} +replace = version: {new_version} diff --git a/.cookiecutterrc b/.cookiecutterrc index 2351690..7eaddf9 100644 --- a/.cookiecutterrc +++ b/.cookiecutterrc @@ -1,14 +1,8 @@ # Generated by cookiepatcher, a small shim around cookiecutter (pip install cookiepatcher) default_context: - allow_tests_inside_package: 'no' - appveyor: 'no' - c_extension_function: '-' - c_extension_module: '-' c_extension_optional: 'no' c_extension_support: 'no' - c_extension_test_pypi: 'no' - c_extension_test_pypi_username: ionelmc codacy: 'no' codacy_projectid: '-' codeclimate: 'no' @@ -18,40 +12,36 @@ default_context: coveralls: 'yes' distribution_name: aspectlib email: contact@ionelmc.ro + formatter_quote_style: single full_name: Ionel Cristian Mărieș + function_name: compute github_actions: 'yes' github_actions_osx: 'no' github_actions_windows: 'no' - legacy_python: 'no' license: BSD 2-Clause License - linter: flake8 + module_name: core package_name: aspectlib pre_commit: 'yes' - pre_commit_formatter: black project_name: Aspectlib project_short_description: '``aspectlib`` is an aspect-oriented programming, monkey-patch and decorators library. It is useful when changing' pypi_badge: 'yes' pypi_disable_upload: 'no' - release_date: '2020-11-15' + release_date: '2022-10-20' repo_hosting: github.com repo_hosting_domain: github.com repo_main_branch: main repo_name: python-aspectlib repo_username: ionelmc - requiresio: 'yes' scrutinizer: 'no' - setup_py_uses_pytest_runner: 'no' setup_py_uses_setuptools_scm: 'no' sphinx_docs: 'yes' sphinx_docs_hosting: https://python-aspectlib.readthedocs.io/ sphinx_doctest: 'yes' - sphinx_theme: sphinx-py3doc-enhanced-theme - test_matrix_configurator: 'no' + sphinx_theme: furo test_matrix_separate_coverage: 'yes' - travis: 'no' - travis_osx: 'no' - version: 1.5.2 + tests_inside_package: 'no' + version: 2.0.0 version_manager: bump2version website: http://blog.ionelmc.ro year_from: '2014' - year_to: '2022' + year_to: '2024' diff --git a/.github/workflows/github-actions.yml b/.github/workflows/github-actions.yml index 4adf1ad..97a257e 100644 --- a/.github/workflows/github-actions.yml +++ b/.github/workflows/github-actions.yml @@ -1,5 +1,5 @@ name: build -on: [push, pull_request] +on: [push, pull_request, workflow_dispatch] jobs: test: name: ${{ matrix.name }} @@ -10,50 +10,26 @@ jobs: matrix: include: - name: 'check' - python: '3.9' - toxpython: 'python3.9' + python: '3.11' + toxpython: 'python3.11' tox_env: 'check' os: 'ubuntu-latest' - name: 'docs' - python: '3.9' - toxpython: 'python3.9' + python: '3.11' + toxpython: 'python3.11' tox_env: 'docs' os: 'ubuntu-latest' - - name: 'py37-cover-release (ubuntu)' - python: '3.7' - toxpython: 'python3.7' - python_arch: 'x64' - tox_env: 'py37-cover-release,codecov' - os: 'ubuntu-latest' - - name: 'py37-cover-debug (ubuntu)' - python: '3.7' - toxpython: 'python3.7' - python_arch: 'x64' - tox_env: 'py37-cover-debug,codecov' - os: 'ubuntu-latest' - - name: 'py37-nocov-release (ubuntu)' - python: '3.7' - toxpython: 'python3.7' - python_arch: 'x64' - tox_env: 'py37-nocov-release' - os: 'ubuntu-latest' - - name: 'py37-nocov-debug (ubuntu)' - python: '3.7' - toxpython: 'python3.7' - python_arch: 'x64' - tox_env: 'py37-nocov-debug' - os: 'ubuntu-latest' - name: 'py38-cover-release (ubuntu)' python: '3.8' toxpython: 'python3.8' python_arch: 'x64' - tox_env: 'py38-cover-release,codecov' + tox_env: 'py38-cover-release' os: 'ubuntu-latest' - name: 'py38-cover-debug (ubuntu)' python: '3.8' toxpython: 'python3.8' python_arch: 'x64' - tox_env: 'py38-cover-debug,codecov' + tox_env: 'py38-cover-debug' os: 'ubuntu-latest' - name: 'py38-nocov-release (ubuntu)' python: '3.8' @@ -71,13 +47,13 @@ jobs: python: '3.9' toxpython: 'python3.9' python_arch: 'x64' - tox_env: 'py39-cover-release,codecov' + tox_env: 'py39-cover-release' os: 'ubuntu-latest' - name: 'py39-cover-debug (ubuntu)' python: '3.9' toxpython: 'python3.9' python_arch: 'x64' - tox_env: 'py39-cover-debug,codecov' + tox_env: 'py39-cover-debug' os: 'ubuntu-latest' - name: 'py39-nocov-release (ubuntu)' python: '3.9' @@ -95,13 +71,13 @@ jobs: python: '3.10' toxpython: 'python3.10' python_arch: 'x64' - tox_env: 'py310-cover-release,codecov' + tox_env: 'py310-cover-release' os: 'ubuntu-latest' - name: 'py310-cover-debug (ubuntu)' python: '3.10' toxpython: 'python3.10' python_arch: 'x64' - tox_env: 'py310-cover-debug,codecov' + tox_env: 'py310-cover-debug' os: 'ubuntu-latest' - name: 'py310-nocov-release (ubuntu)' python: '3.10' @@ -115,17 +91,65 @@ jobs: python_arch: 'x64' tox_env: 'py310-nocov-debug' os: 'ubuntu-latest' + - name: 'py311-cover-release (ubuntu)' + python: '3.11' + toxpython: 'python3.11' + python_arch: 'x64' + tox_env: 'py311-cover-release' + os: 'ubuntu-latest' + - name: 'py311-cover-debug (ubuntu)' + python: '3.11' + toxpython: 'python3.11' + python_arch: 'x64' + tox_env: 'py311-cover-debug' + os: 'ubuntu-latest' + - name: 'py311-nocov-release (ubuntu)' + python: '3.11' + toxpython: 'python3.11' + python_arch: 'x64' + tox_env: 'py311-nocov-release' + os: 'ubuntu-latest' + - name: 'py311-nocov-debug (ubuntu)' + python: '3.11' + toxpython: 'python3.11' + python_arch: 'x64' + tox_env: 'py311-nocov-debug' + os: 'ubuntu-latest' + - name: 'py312-cover-release (ubuntu)' + python: '3.12' + toxpython: 'python3.12' + python_arch: 'x64' + tox_env: 'py312-cover-release' + os: 'ubuntu-latest' + - name: 'py312-cover-debug (ubuntu)' + python: '3.12' + toxpython: 'python3.12' + python_arch: 'x64' + tox_env: 'py312-cover-debug' + os: 'ubuntu-latest' + - name: 'py312-nocov-release (ubuntu)' + python: '3.12' + toxpython: 'python3.12' + python_arch: 'x64' + tox_env: 'py312-nocov-release' + os: 'ubuntu-latest' + - name: 'py312-nocov-debug (ubuntu)' + python: '3.12' + toxpython: 'python3.12' + python_arch: 'x64' + tox_env: 'py312-nocov-debug' + os: 'ubuntu-latest' - name: 'pypy37-cover-release (ubuntu)' python: 'pypy-3.7' toxpython: 'pypy3.7' python_arch: 'x64' - tox_env: 'pypy37-cover-release,codecov' + tox_env: 'pypy37-cover-release' os: 'ubuntu-latest' - name: 'pypy37-cover-debug (ubuntu)' python: 'pypy-3.7' toxpython: 'pypy3.7' python_arch: 'x64' - tox_env: 'pypy37-cover-debug,codecov' + tox_env: 'pypy37-cover-debug' os: 'ubuntu-latest' - name: 'pypy37-nocov-release (ubuntu)' python: 'pypy-3.7' @@ -143,13 +167,13 @@ jobs: python: 'pypy-3.8' toxpython: 'pypy3.8' python_arch: 'x64' - tox_env: 'pypy38-cover-release,codecov' + tox_env: 'pypy38-cover-release' os: 'ubuntu-latest' - name: 'pypy38-cover-debug (ubuntu)' python: 'pypy-3.8' toxpython: 'pypy3.8' python_arch: 'x64' - tox_env: 'pypy38-cover-debug,codecov' + tox_env: 'pypy38-cover-debug' os: 'ubuntu-latest' - name: 'pypy38-nocov-release (ubuntu)' python: 'pypy-3.8' @@ -167,13 +191,13 @@ jobs: python: 'pypy-3.9' toxpython: 'pypy3.9' python_arch: 'x64' - tox_env: 'pypy39-cover-release,codecov' + tox_env: 'pypy39-cover-release' os: 'ubuntu-latest' - name: 'pypy39-cover-debug (ubuntu)' python: 'pypy-3.9' toxpython: 'pypy3.9' python_arch: 'x64' - tox_env: 'pypy39-cover-debug,codecov' + tox_env: 'pypy39-cover-debug' os: 'ubuntu-latest' - name: 'pypy39-nocov-release (ubuntu)' python: 'pypy-3.9' @@ -187,11 +211,35 @@ jobs: python_arch: 'x64' tox_env: 'pypy39-nocov-debug' os: 'ubuntu-latest' + - name: 'pypy310-cover-release (ubuntu)' + python: 'pypy-3.10' + toxpython: 'pypy3.10' + python_arch: 'x64' + tox_env: 'pypy310-cover-release' + os: 'ubuntu-latest' + - name: 'pypy310-cover-debug (ubuntu)' + python: 'pypy-3.10' + toxpython: 'pypy3.10' + python_arch: 'x64' + tox_env: 'pypy310-cover-debug' + os: 'ubuntu-latest' + - name: 'pypy310-nocov-release (ubuntu)' + python: 'pypy-3.10' + toxpython: 'pypy3.10' + python_arch: 'x64' + tox_env: 'pypy310-nocov-release' + os: 'ubuntu-latest' + - name: 'pypy310-nocov-debug (ubuntu)' + python: 'pypy-3.10' + toxpython: 'pypy3.10' + python_arch: 'x64' + tox_env: 'pypy310-nocov-debug' + os: 'ubuntu-latest' steps: - - uses: actions/checkout@v2 + - uses: actions/checkout@v4 with: fetch-depth: 0 - - uses: actions/setup-python@v2 + - uses: actions/setup-python@v5 with: python-version: ${{ matrix.python }} architecture: ${{ matrix.python_arch }} @@ -207,3 +255,14 @@ jobs: TOXPYTHON: '${{ matrix.toxpython }}' run: > tox -e ${{ matrix.tox_env }} -v + finish: + needs: test + if: ${{ always() }} + runs-on: ubuntu-latest + steps: + - uses: coverallsapp/github-action@v2 + with: + parallel-finished: true + - uses: codecov/codecov-action@v3 + with: + CODECOV_TOKEN: ${{ secrets.CODECOV_TOKEN }} diff --git a/.gitignore b/.gitignore index 83a43fd..77973dd 100644 --- a/.gitignore +++ b/.gitignore @@ -1,40 +1,53 @@ *.py[cod] __pycache__ +# Temp files +.*.sw[po] +*~ +*.bak +.DS_Store + # C extensions *.so -# Packages +# Build and package files *.egg *.egg-info -dist -build -eggs +.bootstrap +.build +.cache .eggs -parts +.env +.installed.cfg +.ve bin -var -sdist -wheelhouse +build develop-eggs -.installed.cfg +dist +eggs lib lib64 -venv*/ -pyvenv*/ +parts pip-wheel-metadata/ +pyvenv*/ +sdist +var +venv*/ +wheelhouse # Installer logs pip-log.txt # Unit test / coverage reports +.benchmarks .coverage -.tox .coverage.* +.pytest .pytest_cache/ -nosetests.xml +.tox coverage.xml htmlcov +nosetests.xml # Translations *.mo @@ -43,12 +56,12 @@ htmlcov .mr.developer.cfg # IDE project files +*.iml +*.komodoproject +.idea .project .pydevproject -.idea .vscode -*.iml -*.komodoproject # Complexity output/*.html @@ -57,18 +70,5 @@ output/*/index.html # Sphinx docs/_build -.DS_Store -*~ -.*.sw[po] -.build -.ve -.env -.cache -.pytest -.benchmarks -.bootstrap -.appveyor.token -*.bak - # Mypy Cache .mypy_cache/ diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index 8be60d5..9e9b302 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -1,24 +1,19 @@ -# To install the git pre-commit hook run: -# pre-commit install -# To update the pre-commit hooks run: -# pre-commit install-hooks +# To install the git pre-commit hooks run: +# pre-commit install --install-hooks +# To update the versions: +# pre-commit autoupdate exclude: '^(\.tox|ci/templates|\.bumpversion\.cfg)(/|$)' +# Note the order is intentional to avoid multiple passes of the hooks repos: + - repo: https://github.com/astral-sh/ruff-pre-commit + rev: v0.5.7 + hooks: + - id: ruff + args: [--fix, --exit-non-zero-on-fix, --show-fixes] + - id: ruff-format - repo: https://github.com/pre-commit/pre-commit-hooks - rev: v4.3.0 + rev: v4.6.0 hooks: - id: trailing-whitespace - id: end-of-file-fixer - id: debug-statements - - repo: https://github.com/timothycrosley/isort - rev: 5.10.1 - hooks: - - id: isort - - repo: https://github.com/psf/black - rev: 22.10.0 - hooks: - - id: black - - repo: https://gitlab.com/pycqa/flake8 - rev: 3.9.2 - hooks: - - id: flake8 diff --git a/.readthedocs.yml b/.readthedocs.yml index 59ff5c0..009a913 100644 --- a/.readthedocs.yml +++ b/.readthedocs.yml @@ -3,6 +3,10 @@ version: 2 sphinx: configuration: docs/conf.py formats: all +build: + os: ubuntu-22.04 + tools: + python: "3" python: install: - requirements: docs/requirements.txt diff --git a/CONTRIBUTING.rst b/CONTRIBUTING.rst index 47c71ee..eaff88e 100644 --- a/CONTRIBUTING.rst +++ b/CONTRIBUTING.rst @@ -49,7 +49,7 @@ To set up `python-aspectlib` for local development: Now you can make your changes locally. -4. When you're done making changes run all the checks and docs builder with `tox `_ one command:: +4. When you're done making changes run all the checks and docs builder with one command:: tox @@ -73,8 +73,6 @@ For merging, you should: 3. Add a note to ``CHANGELOG.rst`` about the changes. 4. Add yourself to ``AUTHORS.rst``. - - Tips ---- diff --git a/LICENSE b/LICENSE index 82c46d1..61b141c 100644 --- a/LICENSE +++ b/LICENSE @@ -1,6 +1,6 @@ BSD 2-Clause License -Copyright (c) 2014-2022, Ionel Cristian Mărieș. All rights reserved. +Copyright (c) 2014-2024, Ionel Cristian Mărieș. All rights reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: diff --git a/README.rst b/README.rst index a1bbfa2..415ea7b 100644 --- a/README.rst +++ b/README.rst @@ -10,30 +10,24 @@ Overview * - docs - |docs| * - tests - - | |github-actions| |requires| - | |coveralls| |codecov| + - |github-actions| |coveralls| |codecov| * - package - - | |version| |wheel| |supported-versions| |supported-implementations| - | |commits-since| + - |version| |wheel| |supported-versions| |supported-implementations| |commits-since| .. |docs| image:: https://readthedocs.org/projects/python-aspectlib/badge/?style=flat - :target: https://python-aspectlib.readthedocs.io/ + :target: https://readthedocs.org/projects/python-aspectlib/ :alt: Documentation Status .. |github-actions| image:: https://github.com/ionelmc/python-aspectlib/actions/workflows/github-actions.yml/badge.svg :alt: GitHub Actions Build Status :target: https://github.com/ionelmc/python-aspectlib/actions -.. |requires| image:: https://requires.io/github/ionelmc/python-aspectlib/requirements.svg?branch=main - :alt: Requirements Status - :target: https://requires.io/github/ionelmc/python-aspectlib/requirements/?branch=main - -.. |coveralls| image:: https://coveralls.io/repos/ionelmc/python-aspectlib/badge.svg?branch=main&service=github +.. |coveralls| image:: https://coveralls.io/repos/github/ionelmc/python-aspectlib/badge.svg?branch=main :alt: Coverage Status - :target: https://coveralls.io/r/ionelmc/python-aspectlib + :target: https://coveralls.io/github/ionelmc/python-aspectlib?branch=main .. |codecov| image:: https://codecov.io/gh/ionelmc/python-aspectlib/branch/main/graphs/badge.svg?branch=main :alt: Coverage Status - :target: https://codecov.io/github/ionelmc/python-aspectlib + :target: https://app.codecov.io/github/ionelmc/python-aspectlib .. |version| image:: https://img.shields.io/pypi/v/aspectlib.svg :alt: PyPI Package latest release diff --git a/ci/bootstrap.py b/ci/bootstrap.py index bf9bbd6..f3c9a7e 100755 --- a/ci/bootstrap.py +++ b/ci/bootstrap.py @@ -1,63 +1,57 @@ #!/usr/bin/env python -# -*- coding: utf-8 -*- import os +import pathlib import subprocess import sys -from os.path import abspath -from os.path import dirname -from os.path import exists -from os.path import join -from os.path import relpath -base_path = dirname(dirname(abspath(__file__))) -templates_path = join(base_path, "ci", "templates") +base_path: pathlib.Path = pathlib.Path(__file__).resolve().parent.parent +templates_path = base_path / 'ci' / 'templates' def check_call(args): - print("+", *args) + print('+', *args) subprocess.check_call(args) def exec_in_env(): - env_path = join(base_path, ".tox", "bootstrap") - if sys.platform == "win32": - bin_path = join(env_path, "Scripts") + env_path = base_path / '.tox' / 'bootstrap' + if sys.platform == 'win32': + bin_path = env_path / 'Scripts' else: - bin_path = join(env_path, "bin") - if not exists(env_path): + bin_path = env_path / 'bin' + if not env_path.exists(): import subprocess - print("Making bootstrap env in: {0} ...".format(env_path)) + print(f'Making bootstrap env in: {env_path} ...') try: - check_call([sys.executable, "-m", "venv", env_path]) + check_call([sys.executable, '-m', 'venv', env_path]) except subprocess.CalledProcessError: try: - check_call([sys.executable, "-m", "virtualenv", env_path]) + check_call([sys.executable, '-m', 'virtualenv', env_path]) except subprocess.CalledProcessError: - check_call(["virtualenv", env_path]) - print("Installing `jinja2` into bootstrap environment...") - check_call([join(bin_path, "pip"), "install", "jinja2", "tox"]) - python_executable = join(bin_path, "python") - if not os.path.exists(python_executable): - python_executable += '.exe' + check_call(['virtualenv', env_path]) + print('Installing `jinja2` into bootstrap environment...') + check_call([bin_path / 'pip', 'install', 'jinja2', 'tox']) + python_executable = bin_path / 'python' + if not python_executable.exists(): + python_executable = python_executable.with_suffix('.exe') - print("Re-executing with: {0}".format(python_executable)) - print("+ exec", python_executable, __file__, "--no-env") - os.execv(python_executable, [python_executable, __file__, "--no-env"]) + print(f'Re-executing with: {python_executable}') + print('+ exec', python_executable, __file__, '--no-env') + os.execv(python_executable, [python_executable, __file__, '--no-env']) def main(): import jinja2 - print("Project path: {0}".format(base_path)) + print(f'Project path: {base_path}') jinja = jinja2.Environment( - loader=jinja2.FileSystemLoader(templates_path), + loader=jinja2.FileSystemLoader(str(templates_path)), trim_blocks=True, lstrip_blocks=True, keep_trailing_newline=True, ) - tox_environments = [ line.strip() # 'tox' need not be installed globally, but must be importable @@ -68,22 +62,22 @@ def main(): for line in subprocess.check_output([sys.executable, '-m', 'tox', '--listenvs'], universal_newlines=True).splitlines() ] tox_environments = [line for line in tox_environments if line.startswith('py')] - - for root, _, files in os.walk(templates_path): - for name in files: - relative = relpath(root, templates_path) - with open(join(base_path, relative, name), "w") as fh: - fh.write(jinja.get_template(join(relative, name)).render(tox_environments=tox_environments)) - print("Wrote {}".format(name)) - print("DONE.") + for template in templates_path.rglob('*'): + if template.is_file(): + template_path = template.relative_to(templates_path).as_posix() + destination = base_path / template_path + destination.parent.mkdir(parents=True, exist_ok=True) + destination.write_text(jinja.get_template(template_path).render(tox_environments=tox_environments)) + print(f'Wrote {template_path}') + print('DONE.') -if __name__ == "__main__": +if __name__ == '__main__': args = sys.argv[1:] - if args == ["--no-env"]: + if args == ['--no-env']: main() elif not args: exec_in_env() else: - print("Unexpected arguments {0}".format(args), file=sys.stderr) + print(f'Unexpected arguments: {args}', file=sys.stderr) sys.exit(1) diff --git a/ci/requirements.txt b/ci/requirements.txt index a0ef106..a1708f4 100644 --- a/ci/requirements.txt +++ b/ci/requirements.txt @@ -3,3 +3,4 @@ pip>=19.1.1 setuptools>=18.0.1 six>=1.14.0 tox +twine diff --git a/ci/templates/.github/workflows/github-actions.yml b/ci/templates/.github/workflows/github-actions.yml index b4a7ae2..5274834 100644 --- a/ci/templates/.github/workflows/github-actions.yml +++ b/ci/templates/.github/workflows/github-actions.yml @@ -1,5 +1,5 @@ name: build -on: [push, pull_request] +on: [push, pull_request, workflow_dispatch] jobs: test: name: {{ '${{ matrix.name }}' }} @@ -10,21 +10,21 @@ jobs: matrix: include: - name: 'check' - python: '3.9' - toxpython: 'python3.9' + python: '3.11' + toxpython: 'python3.11' tox_env: 'check' os: 'ubuntu-latest' - name: 'docs' - python: '3.9' - toxpython: 'python3.9' + python: '3.11' + toxpython: 'python3.11' tox_env: 'docs' os: 'ubuntu-latest' {% for env in tox_environments %} {% set prefix = env.split('-')[0] -%} {% if prefix.startswith('pypy') %} -{% set python %}pypy-{{ prefix[4] }}.{{ prefix[5] }}{% endset %} +{% set python %}pypy-{{ prefix[4] }}.{{ prefix[5:] }}{% endset %} {% set cpython %}pp{{ prefix[4:5] }}{% endset %} -{% set toxpython %}pypy{{ prefix[4] }}.{{ prefix[5] }}{% endset %} +{% set toxpython %}pypy{{ prefix[4] }}.{{ prefix[5:] }}{% endset %} {% else %} {% set python %}{{ prefix[2] }}.{{ prefix[3:] }}{% endset %} {% set cpython %}cp{{ prefix[2:] }}{% endset %} @@ -37,15 +37,15 @@ jobs: python: '{{ python }}' toxpython: '{{ toxpython }}' python_arch: '{{ python_arch }}' - tox_env: '{{ env }}{% if 'cover' in env %},codecov{% endif %}' + tox_env: '{{ env }}' os: '{{ os }}-latest' {% endfor %} {% endfor %} steps: - - uses: actions/checkout@v2 + - uses: actions/checkout@v4 with: fetch-depth: 0 - - uses: actions/setup-python@v2 + - uses: actions/setup-python@v5 with: python-version: {{ '${{ matrix.python }}' }} architecture: {{ '${{ matrix.python_arch }}' }} @@ -61,3 +61,14 @@ jobs: TOXPYTHON: '{{ '${{ matrix.toxpython }}' }}' run: > tox -e {{ '${{ matrix.tox_env }}' }} -v + finish: + needs: test + if: {{ '${{ always() }}' }} + runs-on: ubuntu-latest + steps: + - uses: coverallsapp/github-action@v2 + with: + parallel-finished: true + - uses: codecov/codecov-action@v3 + with: + CODECOV_TOKEN: {% raw %}${{ secrets.CODECOV_TOKEN }}{% endraw %} diff --git a/docs/conf.py b/docs/conf.py index 4055513..c243d2e 100644 --- a/docs/conf.py +++ b/docs/conf.py @@ -1,8 +1,3 @@ -# -*- coding: utf-8 -*- - - -import sphinx_py3doc_enhanced_theme - extensions = [ 'sphinx.ext.autodoc', 'sphinx.ext.autosummary', @@ -17,19 +12,19 @@ source_suffix = '.rst' master_doc = 'index' project = 'Aspectlib' -year = '2014-2022' +year = '2014-2024' author = 'Ionel Cristian Mărieș' -copyright = '{0}, {1}'.format(year, author) +copyright = f'{year}, {author}' version = release = '2.0.0' pygments_style = 'trac' templates_path = ['.'] extlinks = { - 'issue': ('https://github.com/ionelmc/python-aspectlib/issues/%s', '#'), - 'pr': ('https://github.com/ionelmc/python-aspectlib/pull/%s', 'PR #'), + 'issue': ('https://github.com/ionelmc/python-aspectlib/issues/%s', '#%s'), + 'pr': ('https://github.com/ionelmc/python-aspectlib/pull/%s', 'PR #%s'), } -html_theme = 'sphinx_py3doc_enhanced_theme' -html_theme_path = [sphinx_py3doc_enhanced_theme.get_html_theme_path()] + +html_theme = 'furo' html_theme_options = { 'githuburl': 'https://github.com/ionelmc/python-aspectlib/', } @@ -37,10 +32,7 @@ html_use_smartypants = True html_last_updated_fmt = '%b %d, %Y' html_split_index = False -html_sidebars = { - '**': ['searchbox.html', 'globaltoc.html', 'sourcelink.html'], -} -html_short_title = '%s-%s' % (project, version) +html_short_title = f'{project}-{version}' napoleon_use_ivar = True napoleon_use_rtype = False diff --git a/docs/index.rst b/docs/index.rst index 72af676..9267902 100644 --- a/docs/index.rst +++ b/docs/index.rst @@ -1,8 +1,6 @@ -Welcome to python-aspectlib's documentation! -============================================ - -``aspectlib`` is an aspect-oriented programming, monkey-patch and decorators library. It is useful when changing -behavior in existing code is desired. +======== +Contents +======== .. toctree:: :maxdepth: 2 diff --git a/docs/readme.rst b/docs/readme.rst new file mode 100644 index 0000000..72a3355 --- /dev/null +++ b/docs/readme.rst @@ -0,0 +1 @@ +.. include:: ../README.rst diff --git a/docs/reference/aspectlib.rst b/docs/reference/aspectlib.rst index 3a4d002..e1db807 100644 --- a/docs/reference/aspectlib.rst +++ b/docs/reference/aspectlib.rst @@ -1,5 +1,5 @@ -Reference: ``aspectlib`` -======================== +aspectlib +========= Overview -------- diff --git a/docs/requirements.txt b/docs/requirements.txt index 62bc14e..c03e307 100644 --- a/docs/requirements.txt +++ b/docs/requirements.txt @@ -1,2 +1,2 @@ sphinx>=1.3 -sphinx-py3doc-enhanced-theme +furo diff --git a/pyproject.toml b/pyproject.toml index fc9d53d..988950a 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,10 +1,54 @@ [build-system] requires = [ "setuptools>=30.3.0", - "wheel", ] -[tool.black] +[tool.ruff] +extend-exclude = ["static", "ci/templates"] line-length = 140 -target-version = ['py37'] -skip-string-normalization = true +src = ["src", "tests"] +target-version = "py38" + +[tool.ruff.lint.per-file-ignores] +"ci/*" = ["S"] + +[tool.ruff.lint] +ignore = [ + "RUF001", # ruff-specific rules ambiguous-unicode-character-string + "S101", # flake8-bandit assert + "S308", # flake8-bandit suspicious-mark-safe-usage + "S603", # flake8-bandit subprocess-without-shell-equals-true + "S607", # flake8-bandit start-process-with-partial-path + "E501", # pycodestyle line-too-long +] +select = [ + "B", # flake8-bugbear + "C4", # flake8-comprehensions + "DTZ", # flake8-datetimez + "E", # pycodestyle errors + "EXE", # flake8-executable + "F", # pyflakes + "I", # isort + "INT", # flake8-gettext + "PIE", # flake8-pie + "PLC", # pylint convention + "PLE", # pylint errors + "PT", # flake8-pytest-style + "PTH", # flake8-use-pathlib + "RSE", # flake8-raise + "RUF", # ruff-specific rules + "S", # flake8-bandit + "UP", # pyupgrade + "W", # pycodestyle warnings +] + +[tool.ruff.lint.flake8-pytest-style] +fixture-parentheses = false +mark-parentheses = false + +[tool.ruff.lint.isort] +forced-separate = ["conftest"] +force-single-line = true + +[tool.ruff.format] +quote-style = "single" diff --git a/setup.cfg b/setup.cfg deleted file mode 100644 index e617fb4..0000000 --- a/setup.cfg +++ /dev/null @@ -1,11 +0,0 @@ -[flake8] -max-line-length = 140 -exclude = .tox,.eggs,ci/templates,build,dist - -[tool:isort] -force_single_line = True -line_length = 120 -known_first_party = aspectlib -default_section = THIRDPARTY -forced_separate = test_aspectlib -skip = .tox,.eggs,ci/templates,build,dist diff --git a/setup.py b/setup.py index 0fd4847..2cb466b 100755 --- a/setup.py +++ b/setup.py @@ -1,20 +1,13 @@ #!/usr/bin/env python -# -*- encoding: utf-8 -*- - -import io import re -from glob import glob -from os.path import basename -from os.path import dirname -from os.path import join -from os.path import splitext +from pathlib import Path from setuptools import find_packages from setuptools import setup def read(*names, **kwargs): - with io.open(join(dirname(__file__), *names), encoding=kwargs.get('encoding', 'utf8')) as fh: + with Path(__file__).parent.joinpath(*names).open(encoding=kwargs.get('encoding', 'utf8')) as fh: return fh.read() @@ -32,7 +25,7 @@ def read(*names, **kwargs): url='https://github.com/ionelmc/python-aspectlib', packages=find_packages('src'), package_dir={'': 'src'}, - py_modules=[splitext(basename(path))[0] for path in glob('src/*.py')], + py_modules=[path.stem for path in Path('src').glob('*.py')], include_package_data=True, zip_safe=False, classifiers=[ @@ -46,10 +39,11 @@ def read(*names, **kwargs): 'Programming Language :: Python', 'Programming Language :: Python :: 3', 'Programming Language :: Python :: 3 :: Only', - '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.12', 'Programming Language :: Python :: Implementation :: CPython', 'Programming Language :: Python :: Implementation :: PyPy', # uncomment if you test on these interpreters: @@ -86,9 +80,15 @@ def read(*names, **kwargs): 'mocking', 'logger', ], - python_requires='>=3.7', - install_requires=['fields'], - extras_require={}, + python_requires='>=3.8', + install_requires=[ + # eg: "aspectlib==1.1.1", "six>=1.7", + ], + extras_require={ + # eg: + # "rst": ["docutils>=0.11"], + # ":python_version=='3.8'": ["backports.zoneinfo"], + }, entry_points={ 'pytest11': ['aspectlib = aspectlib.pytestsupport'], }, diff --git a/tox.ini b/tox.ini index 0b23773..1900e6f 100644 --- a/tox.ini +++ b/tox.ini @@ -7,26 +7,27 @@ commands = python ci/bootstrap.py --no-env passenv = * -; a generative tox configuration, see: https://tox.readthedocs.io/en/latest/config.html#generative-envlist +; a generative tox configuration, see: https://tox.wiki/en/latest/user_guide.html#generative-environments [tox] envlist = clean, check, docs, - {py37,py38,py39,py310,pypy37,pypy38,pypy39}-{cover,nocov}-{release,debug}, + {py38,py39,py310,py311,py312,pypy37,pypy38,pypy39,pypy310}-{cover,nocov}-{release,debug}, report ignore_basepython_conflict = true [testenv] basepython = - pypy37: {env:TOXPYTHON:pypy3.7} pypy38: {env:TOXPYTHON:pypy3.8} pypy39: {env:TOXPYTHON:pypy3.9} - py37: {env:TOXPYTHON:python3.7} + pypy310: {env:TOXPYTHON:pypy3.10} py38: {env:TOXPYTHON:python3.8} py39: {env:TOXPYTHON:python3.9} py310: {env:TOXPYTHON:python3.10} + py311: {env:TOXPYTHON:python3.11} + py312: {env:TOXPYTHON:python3.12} {bootstrap,clean,check,report,docs,codecov,coveralls}: {env:TOXPYTHON:python3} setenv = PYTHONPATH={toxinidir}/tests @@ -48,22 +49,21 @@ deps = cover: pytest-cov commands = nocov: {posargs:pytest -vv --ignore=src} - cover: {posargs:pytest --cov --cov-report=term-missing -vv} + cover: {posargs:pytest --cov --cov-report=term-missing --cov-report=xml -vv} [testenv:check] deps = docutils check-manifest - flake8 + pre-commit readme-renderer pygments isort skip_install = true commands = python setup.py check --strict --metadata --restructuredtext - check-manifest {toxinidir} - flake8 - isort --verbose --check-only --diff --filter-files . + check-manifest . + pre-commit run --all-files --show-diff-on-failure [testenv:docs] usedevelop = true @@ -74,20 +74,6 @@ commands = sphinx-build {posargs:-E} -b html docs dist/docs sphinx-build -b linkcheck docs dist/docs -[testenv:coveralls] -deps = - coveralls -skip_install = true -commands = - coveralls [] - -[testenv:codecov] -deps = - codecov -skip_install = true -commands = - codecov [] - [testenv:report] deps = coverage @@ -97,7 +83,10 @@ commands = coverage html [testenv:clean] -commands = coverage erase +commands = + python setup.py clean + coverage erase skip_install = true deps = + setuptools coverage From e85fc6cf3febb9f891f19df5eab6a71eea13a7fd Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ionel=20Cristian=20M=C4=83rie=C8=99?= Date: Mon, 12 Aug 2024 18:08:57 +0300 Subject: [PATCH 174/179] Apply ruff cleanups/formatting. --- .pre-commit-config.yaml | 2 +- src/aspectlib/__init__.py | 98 +++++++------- src/aspectlib/contrib.py | 13 +- src/aspectlib/debug.py | 68 +++++----- src/aspectlib/test.py | 80 ++++++----- src/aspectlib/utils.py | 35 ++--- tests/mymod.py | 4 +- tests/test_aspectlib.py | 111 ++++++++-------- tests/test_aspectlib_debug.py | 16 +-- tests/test_aspectlib_py3.py | 3 - tests/test_aspectlib_py37.py | 14 +- tests/test_aspectlib_test.py | 182 +++++++++++++------------- tests/test_contrib.py | 18 +-- tests/test_integrations.py | 44 +++---- tests/test_pkg1/test_pkg2/test_mod.py | 2 +- tests/test_pytestsupport.py | 2 +- 16 files changed, 340 insertions(+), 352 deletions(-) diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index 9e9b302..0e3a2cd 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -9,7 +9,7 @@ repos: rev: v0.5.7 hooks: - id: ruff - args: [--fix, --exit-non-zero-on-fix, --show-fixes] + args: [--fix, --exit-non-zero-on-fix, --show-fixes, --unsafe-fixes] - id: ruff-format - repo: https://github.com/pre-commit/pre-commit-hooks rev: v4.6.0 diff --git a/src/aspectlib/__init__.py b/src/aspectlib/__init__.py index f57cbbf..bd70924 100644 --- a/src/aspectlib/__init__.py +++ b/src/aspectlib/__init__.py @@ -85,7 +85,7 @@ class UnsupportedType(TypeError): pass -class Proceed(object): +class Proceed: """ Instruction for calling the decorated function. Can be used multiple times. @@ -99,7 +99,7 @@ def __init__(self, *args, **kwargs): self.kwargs = kwargs -class Return(object): +class Return: """ Instruction for returning a *optional* value. @@ -112,7 +112,7 @@ def __init__(self, value): self.value = value -class Aspect(object): +class Aspect: """ Container for the advice yielding generator. Can be used as a decorator on other function to change behavior according to the advices yielded from the generator. @@ -161,13 +161,13 @@ def __new__(cls, advising_function=UNSPECIFIED, bind=False): if advising_function is UNSPECIFIED: return partial(cls, bind=bind) else: - self = super(Aspect, cls).__new__(cls) + self = super().__new__(cls) self.__init__(advising_function, bind) return self def __init__(self, advising_function, bind=False): if not isgeneratorfunction(advising_function): - raise ExpectedGeneratorFunction("advising_function %s must be a generator function." % advising_function) + raise ExpectedGeneratorFunction(f'advising_function {advising_function} must be a generator function.') self.advising_function = advising_function self.bind = bind @@ -181,7 +181,7 @@ async def advising_asyncgenerator_wrapper_py35(*args, **kwargs): else: advisor = self.advising_function(*args, **kwargs) if not isgenerator(advisor): - raise ExpectedGenerator("advising_function %s did not return a generator." % self.advising_function) + raise ExpectedGenerator(f'advising_function {self.advising_function} did not return a generator.') try: advice = next(advisor) while True: @@ -207,7 +207,7 @@ async def advising_asyncgenerator_wrapper_py35(*args, **kwargs): elif isinstance(advice, Return): return advice.value else: - raise UnacceptableAdvice("Unknown advice %s" % advice) + raise UnacceptableAdvice(f'Unknown advice {advice}') finally: advisor.close() @@ -221,7 +221,7 @@ def advising_generator_wrapper_py35(*args, **kwargs): else: advisor = self.advising_function(*args, **kwargs) if not isgenerator(advisor): - raise ExpectedGenerator("advising_function %s did not return a generator." % self.advising_function) + raise ExpectedGenerator(f'advising_function {self.advising_function} did not return a generator.') try: advice = next(advisor) while True: @@ -247,7 +247,7 @@ def advising_generator_wrapper_py35(*args, **kwargs): elif isinstance(advice, Return): return advice.value else: - raise UnacceptableAdvice("Unknown advice %s" % advice) + raise UnacceptableAdvice(f'Unknown advice {advice}') finally: advisor.close() @@ -260,7 +260,7 @@ def advising_function_wrapper(*args, **kwargs): else: advisor = self.advising_function(*args, **kwargs) if not isgenerator(advisor): - raise ExpectedGenerator("advising_function %s did not return a generator." % self.advising_function) + raise ExpectedGenerator(f'advising_function {self.advising_function} did not return a generator.') try: advice = next(advisor) while True: @@ -283,23 +283,23 @@ def advising_function_wrapper(*args, **kwargs): elif isinstance(advice, Return): return advice.value else: - raise UnacceptableAdvice("Unknown advice %s" % advice) + raise UnacceptableAdvice(f'Unknown advice {advice}') finally: advisor.close() return mimic(advising_function_wrapper, cutpoint_function) -class Fabric(object): +class Fabric: pass -class Rollback(object): +class Rollback: """ When called, rollbacks all the patches and changes the :func:`weave` has done. """ - __slots__ = '_rollbacks' + __slots__ = ('_rollbacks',) def __init__(self, rollback=None): if rollback is None: @@ -323,7 +323,7 @@ def __exit__(self, *_): rollback = __call__ = __exit__ -class ObjectBag(object): +class ObjectBag: def __init__(self): self._objects = {} @@ -336,10 +336,10 @@ def has(self, obj): return False -BrokenBag = type('BrokenBag', (), dict(has=lambda self, obj: False))() +BrokenBag = type('BrokenBag', (), {'has': lambda self, obj: False})() -class EmptyRollback(object): +class EmptyRollback: def __enter__(self): return self @@ -356,20 +356,20 @@ def _checked_apply(aspects, function, module=None): logdebug(' applying aspects %s to function %s.', aspects, function) if callable(aspects): wrapper = aspects(function) - assert callable(wrapper), 'Aspect %s did not return a callable (it return %s).' % (aspects, wrapper) + assert callable(wrapper), f'Aspect {aspects} did not return a callable (it return {wrapper}).' else: wrapper = function for aspect in aspects: wrapper = aspect(wrapper) - assert callable(wrapper), 'Aspect %s did not return a callable (it return %s).' % (aspect, wrapper) + assert callable(wrapper), f'Aspect {aspect} did not return a callable (it return {wrapper}).' return mimic(wrapper, function, module=module) def _check_name(name): if not VALID_IDENTIFIER.match(name): raise SyntaxError( - "Could not match %r to %r. It should be a string of " - "letters, numbers and underscore that starts with a letter or underscore." % (name, VALID_IDENTIFIER.pattern) + f'Could not match {name!r} to {VALID_IDENTIFIER.pattern!r}. It should be a string of ' + 'letters, numbers and underscore that starts with a letter or underscore.' ) @@ -408,12 +408,12 @@ def weave(target, aspects, **options): """ if not callable(aspects): if not hasattr(aspects, '__iter__'): - raise ExpectedAdvice('%s must be an `Aspect` instance, a callable or an iterable of.' % aspects) + raise ExpectedAdvice(f'{aspects} must be an `Aspect` instance, a callable or an iterable of.') for obj in aspects: if not callable(obj): - raise ExpectedAdvice('%s must be an `Aspect` instance or a callable.' % obj) - assert target, "Can't weave falsy value %r." % target - logdebug("weave (target=%s, aspects=%s, **options=%s)", target, aspects, options) + raise ExpectedAdvice(f'{obj} must be an `Aspect` instance or a callable.') + assert target, f"Can't weave falsy value {target!r}." + logdebug('weave (target=%s, aspects=%s, **options=%s)', target, aspects, options) bag = options.setdefault('bag', ObjectBag()) @@ -436,7 +436,7 @@ def weave(target, aspects, **options): else: break else: - raise ImportError("Could not import %r. Last try was for %s" % (target, owner)) + raise ImportError(f'Could not import {target!r}. Last try was for {owner}') if '.' in name: path, name = name.rsplit('.', 1) @@ -444,14 +444,14 @@ def weave(target, aspects, **options): while path: owner = getattr(owner, path.popleft()) - logdebug("@ patching %s from %s ...", name, owner) + logdebug('@ patching %s from %s ...', name, owner) obj = getattr(owner, name) if isinstance(obj, (type, ClassType)): - logdebug(" .. as a class %r.", obj) + logdebug(' .. as a class %r.', obj) return weave_class(obj, aspects, owner=owner, name=name, **options) elif callable(obj): # or isinstance(obj, FunctionType) ?? - logdebug(" .. as a callable %r.", obj) + logdebug(' .. as a callable %r.', obj) if bag.has(obj): return Nothing return patch_module_function(owner, obj, aspects, force_name=name, **options) @@ -468,7 +468,7 @@ def weave(target, aspects, **options): return Nothing inst = target.__self__ name = target.__name__ - logdebug("@ patching %r (%s) as instance method.", target, name) + logdebug('@ patching %r (%s) as instance method.', target, name) func = target.__func__ setattr(inst, name, _checked_apply(aspects, func).__get__(inst, type(inst))) return Rollback(lambda: delattr(inst, name)) @@ -480,7 +480,7 @@ def weave(target, aspects, **options): while path: owner = getattr(owner, path.popleft()) name = target.__name__ - logdebug("@ patching %r (%s) as a property.", target, name) + logdebug('@ patching %r (%s) as a property.', target, name) func = owner.__dict__[name] return patch_module(owner, name, _checked_apply(aspects, func), func, **options) elif isclass(target): @@ -490,7 +490,7 @@ def weave(target, aspects, **options): elif type(target).__module__ not in ('builtins', '__builtin__') or InstanceType and isinstance(target, InstanceType): return weave_instance(target, aspects, **options) else: - raise UnsupportedType("Can't weave object %s of type %s" % (target, type(target))) + raise UnsupportedType(f"Can't weave object {target} of type {type(target)}") def _rewrap_method(func, klass, aspect): @@ -521,12 +521,12 @@ def weave_instance(instance, aspect, methods=NORMAL_METHODS, lazy=False, bag=Bro entanglement = Rollback() method_matches = make_method_matcher(methods) - logdebug("weave_instance (module=%r, aspect=%s, methods=%s, lazy=%s, **options=%s)", instance, aspect, methods, lazy, options) + logdebug('weave_instance (module=%r, aspect=%s, methods=%s, lazy=%s, **options=%s)', instance, aspect, methods, lazy, options) def fixup(func): return func.__get__(instance, type(instance)) - fixed_aspect = aspect + [fixup] if isinstance(aspect, (list, tuple)) else [aspect, fixup] + fixed_aspect = [*aspect, fixup] if isinstance(aspect, (list, tuple)) else [aspect, fixup] for attr in dir(instance): if method_matches(attr): @@ -553,7 +553,7 @@ def weave_module(module, aspect, methods=NORMAL_METHODS, lazy=False, bag=BrokenB entanglement = Rollback() method_matches = make_method_matcher(methods) - logdebug("weave_module (module=%r, aspect=%s, methods=%s, lazy=%s, **options=%s)", module, aspect, methods, lazy, options) + logdebug('weave_module (module=%r, aspect=%s, methods=%s, lazy=%s, **options=%s)', module, aspect, methods, lazy, options) for attr in dir(module): if method_matches(attr): @@ -578,7 +578,7 @@ def weave_class( .. warning:: You should not use this directly. """ - assert isclass(klass), "Can't weave %r. Must be a class." % klass + assert isclass(klass), f"Can't weave {klass!r}. Must be a class." if bag.has(klass): return Nothing @@ -586,7 +586,7 @@ def weave_class( entanglement = Rollback() method_matches = make_method_matcher(methods) logdebug( - "weave_class (klass=%r, methods=%s, subclasses=%s, lazy=%s, owner=%s, name=%s, aliases=%s, bases=%s)", + 'weave_class (klass=%r, methods=%s, subclasses=%s, lazy=%s, owner=%s, name=%s, aliases=%s, bases=%s)', klass, methods, subclasses, @@ -600,7 +600,7 @@ def weave_class( if subclasses and hasattr(klass, '__subclasses__'): sub_targets = klass.__subclasses__() if sub_targets: - logdebug("~ weaving subclasses: %s", sub_targets) + logdebug('~ weaving subclasses: %s', sub_targets) for sub_class in sub_targets: if not issubclass(sub_class, Fabric): entanglement.merge(weave_class(sub_class, aspect, methods=methods, subclasses=subclasses, lazy=lazy, bag=bag)) @@ -620,7 +620,7 @@ def __init__(self, *args, **kwargs): if ismethoddescriptor(func): wrappers[attr] = _rewrap_method(func, klass, aspect) - logdebug(" * creating subclass with attributes %r", wrappers) + logdebug(' * creating subclass with attributes %r', wrappers) name = name or klass.__name__ SubClass = type(name, (klass, Fabric), wrappers) SubClass.__module__ = klass.__module__ @@ -631,7 +631,7 @@ def __init__(self, *args, **kwargs): for attr, func in klass.__dict__.items(): if method_matches(attr): if isroutine(func): - logdebug("@ patching attribute %r (original: %r).", attr, func) + logdebug('@ patching attribute %r (original: %r).', attr, func) setattr(klass, attr, _rewrap_method(func, klass, aspect)) else: continue @@ -644,7 +644,7 @@ def __init__(self, *args, **kwargs): for attr, func in sklass.__dict__.items(): if method_matches(attr) and attr not in original and attr not in super_original: if isroutine(func): - logdebug("@ patching attribute %r (from superclass: %s, original: %r).", attr, sklass.__name__, func) + logdebug('@ patching attribute %r (from superclass: %s, original: %r).', attr, sklass.__name__, func) setattr(klass, attr, _rewrap_method(func, sklass, aspect)) else: continue @@ -692,31 +692,31 @@ def patch_module(module, name, replacement, original=UNSPECIFIED, aliases=True, except (TypeError, AttributeError): pass for alias in dir(module): - logdebug("alias:%s (%s)", alias, name) + logdebug('alias:%s (%s)', alias, name) if hasattr(module, alias): obj = getattr(module, alias) - logdebug("- %s:%s (%s)", obj, original, obj is original) + logdebug('- %s:%s (%s)', obj, original, obj is original) if obj is original: if aliases or alias == name: - logdebug("= saving %s on %s.%s ...", replacement, target, alias) + logdebug('= saving %s on %s.%s ...', replacement, target, alias) setattr(module, alias, replacement) rollback.merge(lambda alias=alias: setattr(module, alias, original)) if alias == name: seen = True elif alias == name: if ismethod(obj): - logdebug("= saving %s on %s.%s ...", replacement, target, alias) + logdebug('= saving %s on %s.%s ...', replacement, target, alias) setattr(module, alias, replacement) rollback.merge(lambda alias=alias: setattr(module, alias, original)) seen = True else: - raise AssertionError("%s.%s = %s is not %s." % (module, alias, obj, original)) + raise AssertionError(f'{module}.{alias} = {obj} is not {original}.') if not seen: warnings.warn( - 'Setting %s.%s to %s. There was no previous definition, probably patching the wrong module.' % (target, name, replacement) + f'Setting {target}.{name} to {replacement}. There was no previous definition, probably patching the wrong module.', stacklevel=2 ) - logdebug("= saving %s on %s.%s ...", replacement, target, name) + logdebug('= saving %s on %s.%s ...', replacement, target, name) setattr(module, name, replacement) rollback.merge(lambda: setattr(module, name, original)) return rollback @@ -731,7 +731,7 @@ def patch_module_function(module, target, aspect, force_name=None, bag=BrokenBag :returns: An :obj:`aspectlib.Rollback` object. """ logdebug( - "patch_module_function (module=%s, target=%s, aspect=%s, force_name=%s, **options=%s", module, target, aspect, force_name, options + 'patch_module_function (module=%s, target=%s, aspect=%s, force_name=%s, **options=%s', module, target, aspect, force_name, options ) name = force_name or target.__name__ return patch_module(module, name, _checked_apply(aspect, target, module=module), original=target, **options) diff --git a/src/aspectlib/contrib.py b/src/aspectlib/contrib.py index 727bf0c..298bf0d 100644 --- a/src/aspectlib/contrib.py +++ b/src/aspectlib/contrib.py @@ -53,8 +53,15 @@ def retry_aspect(cutpoint, *args, **kwargs): timeout = backoff else: timeout = backoff(count) - logger.exception("%s(%s, %s) raised exception %s. %s retries left. Sleeping %s secs.", - cutpoint.__name__, args, kwargs, exc, retries - count, timeout) + logger.exception( + '%s(%s, %s) raised exception %s. %s retries left. Sleeping %s secs.', + cutpoint.__name__, + args, + kwargs, + exc, + retries - count, + timeout, + ) sleep(timeout) return retry_aspect if func is None else retry_aspect(func) @@ -64,7 +71,7 @@ def exponential_backoff(count): """ Wait 2**N seconds. """ - return 2 ** count + return 2**count retry.exponential_backoff = exponential_backoff diff --git a/src/aspectlib/debug.py b/src/aspectlib/debug.py index 7e030a1..af589bc 100644 --- a/src/aspectlib/debug.py +++ b/src/aspectlib/debug.py @@ -28,11 +28,10 @@ def format_stack(skip=0, length=6, _sep=os.path.sep): """ Returns a one-line string with the current callstack. """ - return ' < '.join("%s:%s:%s" % ( - '/'.join(f.f_code.co_filename.split(_sep)[-2:]), - f.f_lineno, - f.f_code.co_name - ) for f in islice(frame_iterator(sys._getframe(1 + skip)), length)) + return ' < '.join( + '{}:{}:{}'.format('/'.join(f.f_code.co_filename.split(_sep)[-2:]), f.f_lineno, f.f_code.co_name) + for f in islice(frame_iterator(sys._getframe(1 + skip)), length) + ) PRINTABLE = string.digits + string.ascii_letters + string.punctuation + ' ' @@ -46,20 +45,22 @@ def strip_non_ascii(val): return str(val).translate(ASCII_ONLY) -def log(func=None, - stacktrace=10, - stacktrace_align=60, - attributes=(), - module=True, - call=True, - call_args=True, - call_args_repr=repr, - result=True, - exception=True, - exception_repr=repr, - result_repr=strip_non_ascii, - use_logging='CRITICAL', - print_to=None): +def log( + func=None, + stacktrace=10, + stacktrace_align=60, + attributes=(), + module=True, + call=True, + call_args=True, + call_args_repr=repr, + result=True, + exception=True, + exception_repr=repr, + result_repr=strip_non_ascii, + use_logging='CRITICAL', + print_to=None, +): """ Decorates `func` to have logging. @@ -145,9 +146,9 @@ def log(func=None, Added `call` option. """ - loglevel = use_logging and ( - logging._levelNames if hasattr(logging, '_levelNames') else logging._nameToLevel - ).get(use_logging, logging.CRITICAL) + loglevel = use_logging and (logging._levelNames if hasattr(logging, '_levelNames') else logging._nameToLevel).get( + use_logging, logging.CRITICAL + ) _missing = object() def dump(buf): @@ -168,7 +169,7 @@ class __logged__(Aspect): def __init__(self, cutpoint_function, binding=None): mimic(self, cutpoint_function) self.cutpoint_function = cutpoint_function - self.final_function = super(__logged__, self).__call__(cutpoint_function) + self.final_function = super().__call__(cutpoint_function) self.binding = binding def __get__(self, instance, owner): @@ -194,26 +195,21 @@ def advising_function(self, *args, **kwargs): callarg = False val = getattr(instance, key, _missing) if val is not _missing and key != name: - info.append(' %s=%s' % ( - key, call_args_repr(val() if callarg else val) - )) - sig = buf = '{%s%s%s}.%s' % ( - instance_type.__module__ + '.' if module else '', - instance_type.__name__, - ''.join(info), - name + info.append(f' {key}={call_args_repr(val() if callarg else val)}') + sig = buf = '{{{}{}{}}}.{}'.format( + instance_type.__module__ + '.' if module else '', instance_type.__name__, ''.join(info), name ) else: sig = buf = name if call_args: - buf += '(%s%s)' % ( + buf += '({}{})'.format( ', '.join(repr(i) for i in (args if call_args is True else args[:call_args])), - ((', ' if args else '') + ', '.join('%s=%r' % i for i in kwargs.items())) + ((', ' if args else '') + ', '.join('{}={!r}'.format(*i) for i in kwargs.items())) if kwargs and call_args is True else '', ) if stacktrace: - buf = ("%%-%ds <<< %%s" % stacktrace_align) % (buf, format_stack(skip=1, length=stacktrace)) + buf = ('%%-%ds <<< %%s' % stacktrace_align) % (buf, format_stack(skip=1, length=stacktrace)) if call: dump(buf) try: @@ -222,11 +218,11 @@ def advising_function(self, *args, **kwargs): if exception: if not call: dump(buf) - dump('%s ~ raised %s' % (sig, exception_repr(exc))) + dump(f'{sig} ~ raised {exception_repr(exc)}') raise if result: - dump('%s => %s' % (sig, result_repr(res))) + dump(f'{sig} => {result_repr(res)}') if func: return __logged__(func) diff --git a/src/aspectlib/test.py b/src/aspectlib/test.py index 8f27181..5544935 100644 --- a/src/aspectlib/test.py +++ b/src/aspectlib/test.py @@ -37,7 +37,7 @@ from collections import ChainMap from collections import OrderedDict -__all__ = 'mock', 'record', "Story" +__all__ = 'mock', 'record', 'Story' logger = getLogger(__name__) logexception = logf(logger.exception) @@ -46,7 +46,7 @@ CallEx = namedtuple('CallEx', ('self', 'name', 'args', 'kwargs')) Result = namedtuple('Result', ('self', 'args', 'kwargs', 'result', 'exception')) ResultEx = namedtuple('ResultEx', ('self', 'name', 'args', 'kwargs', 'result', 'exception')) -_INIT = Sentinel("INIT") +_INIT = Sentinel('INIT') def mock(return_value, call=False): @@ -73,7 +73,7 @@ def mock_wrapper(*args, **kwargs): return mock_decorator -class LogCapture(object): +class LogCapture: """ Records all log messages made on the given logger. Assumes the logger has a ``_log`` method. @@ -152,7 +152,7 @@ def messages(self): def has(self, message, *args, **kwargs): level = kwargs.pop('level', None) - assert not kwargs, "Unexpected arguments: %s" % kwargs + assert not kwargs, f'Unexpected arguments: {kwargs}' for call_final_message, call_message, call_args, call_level in self._calls: if level is None or level == call_level: if message == call_message and args == call_args if args else message == call_final_message or message == call_message: @@ -162,12 +162,11 @@ def has(self, message, *args, **kwargs): def assertLogged(self, message, *args, **kwargs): if not self.has(message, *args, **kwargs): raise AssertionError( - "There's no such message %r (with args %r) logged on %s. Logged messages where: %s" - % (message, args, self._logger, self.calls) + f"There's no such message {message!r} (with args {args!r}) logged on {self._logger}. Logged messages where: {self.calls}" ) -class _RecordingFunctionWrapper(object): +class _RecordingFunctionWrapper: """ Function wrapper that records calls and can be used as an weaver context manager. @@ -175,7 +174,7 @@ class _RecordingFunctionWrapper(object): """ def __init__(self, wrapped, iscalled=True, calls=None, callback=None, extended=False, results=False, recurse_lock=None, binding=None): - assert not results or iscalled, "`iscalled` must be True if `results` is True" + assert not results or iscalled, '`iscalled` must be True if `results` is True' mimic(self, wrapped) self.__wrapped = wrapped self.__entanglement = None @@ -295,8 +294,8 @@ def record(func=None, recurse_lock_factory=allocate_lock, **options): return partial(record, **options) -class StoryResultWrapper(object): - __slots__ = '__recorder__' +class StoryResultWrapper: + __slots__ = ('__recorder__',) # def __init__(self, recorder): self.__recorder__ = recorder @@ -306,11 +305,11 @@ def __eq__(self, result): def __pow__(self, exception): if not (isinstance(exception, BaseException) or isclass(exception) and issubclass(exception, BaseException)): - raise RuntimeError("Value %r must be an exception type or instance." % exception) + raise RuntimeError(f'Value {exception!r} must be an exception type or instance.') self.__recorder__(_Raises(exception)) def __unsupported__(self, *args): - raise TypeError("Unsupported operation. Only `==` (for results) and `**` (for exceptions) can be used.") + raise TypeError('Unsupported operation. Only `==` (for results) and `**` (for exceptions) can be used.') for mm in ( '__add__', @@ -382,10 +381,10 @@ def __unsupported__(self, *args): '__rcmp__', '__nonzero__', ): - exec("%s = __unsupported__" % mm) + exec(f'{mm} = __unsupported__') # noqa: S102 -class _StoryFunctionWrapper(object): +class _StoryFunctionWrapper: def __init__(self, wrapped, handle, binding=None, owner=None): self._wrapped = wrapped self._name = wrapped.__name__ @@ -429,7 +428,7 @@ def __call__(self, *args, **kwargs): return self._handle(self._binding, self._name, args, kwargs, self._wrapped) -class _RecordingBase(object): +class _RecordingBase: _target = None _options = None @@ -443,13 +442,13 @@ def __init__(self, target, **options): def _make_key(self, binding, name, args, kwargs): if binding is not None: binding, _ = self._ids[id(binding)] - return (binding, name, ', '.join(repr_ex(i) for i in args), ', '.join("%s=%s" % (k, repr_ex(v)) for k, v in kwargs.items())) + return (binding, name, ', '.join(repr_ex(i) for i in args), ', '.join(f'{k}={repr_ex(v)}' for k, v in kwargs.items())) def _tag_result(self, name, result): if isinstance(result, _Binds): instance_name = camelcase_to_underscores(name.rsplit('.', 1)[-1]) self._instances[instance_name] += 1 - instance_name = "%s_%s" % (instance_name, self._instances[instance_name]) + instance_name = f'{instance_name}_{self._instances[instance_name]}' self._ids[id(result.value)] = instance_name, result.value result.value = instance_name else: @@ -459,14 +458,9 @@ def _tag_result(self, name, result): def _handle(self, binding, name, args, kwargs, result): pk = self._make_key(binding, name, args, kwargs) result = self._tag_result(name, result) - assert ( - pk not in self._calls or self._calls[pk] == result - ), "Story creation inconsistency. There is already a result cached for " "binding:%r name:%r args:%r kwargs:%r and it's: %r." % ( - binding, - name, - args, - kwargs, - self._calls[pk], + assert pk not in self._calls or self._calls[pk] == result, ( + 'Story creation inconsistency. There is already a result cached for ' + f"binding:{binding!r} name:{name!r} args:{args!r} kwargs:{kwargs!r} and it's: {self._calls[pk]!r}." ) self._calls[pk] = result @@ -480,9 +474,9 @@ def __exit__(self, *args): del self._ids -_Raises = container("Raises") -_Returns = container("Returns") -_Binds = container("Binds") +_Raises = container('Raises') +_Returns = container('Returns') +_Binds = container('Binds') class Story(_RecordingBase): @@ -531,7 +525,7 @@ class Story(_RecordingBase): _FunctionWrapper = _StoryFunctionWrapper def __init__(self, *args, **kwargs): - super(Story, self).__init__(*args, **kwargs) + super().__init__(*args, **kwargs) frame = _getframe(1) self._context = frame.f_globals, frame.f_locals @@ -577,14 +571,14 @@ def replay(self, **options): return Replay(self, **options) -ReplayPair = namedtuple("ReplayPair", ('expected', 'actual')) +ReplayPair = namedtuple('ReplayPair', ('expected', 'actual')) def logged_eval(value, context): try: - return eval(value, *context) - except: # noqa - logexception("Failed to evaluate %r.\nContext:\n%s", value, ''.join(format_stack(f=_getframe(1), limit=15))) + return eval(value, *context) # noqa: S307 + except: + logexception('Failed to evaluate %r.\nContext:\n%s', value, ''.join(format_stack(f=_getframe(1), limit=15))) raise @@ -599,7 +593,7 @@ class Replay(_RecordingBase): _FunctionWrapper = _ReplayFunctionWrapper def __init__(self, play, proxy=True, strict=True, dump=True, recurse_lock=False, **options): - super(Replay, self).__init__(play._target, **options) + super().__init__(play._target, **options) self._calls, self._expected, self._actual = ChainMap(self._calls, play._calls), play._calls, self._calls self._proxy = proxy @@ -619,7 +613,7 @@ def _handle(self, binding, name, args, kwargs, wrapped, bind=None): elif isinstance(result, _Raises): raise logged_eval(result.value, self._context) else: - raise RuntimeError('Internal failure - unknown result: %r' % result) # pragma: no cover + raise RuntimeError(f'Internal failure - unknown result: {result!r}') # pragma: no cover else: if self._proxy: shouldrecord = not self._recurse_lock or self._recurse_lock.acquire(False) @@ -640,7 +634,7 @@ def _handle(self, binding, name, args, kwargs, wrapped, bind=None): if shouldrecord and self._recurse_lock: self._recurse_lock.release() else: - raise AssertionError("Unexpected call to %s/%s with args:%s kwargs:%s" % pk) + raise AssertionError('Unexpected call to {}/{} with args:{} kwargs:{}'.format(*pk)) def _unexpected(self, _missing=False): if _missing: @@ -718,7 +712,7 @@ def expected(self): return ''.join(_format_calls(self._expected)) def __exit__(self, *exception): - super(Replay, self).__exit__() + super().__exit__() if self._strict or self._dump: diff = self.diff if diff: @@ -733,17 +727,17 @@ def __exit__(self, *exception): def _format_calls(calls): for (binding, name, args, kwargs), result in calls.items(): - sig = '%s(%s%s%s)' % (name, args, ', ' if kwargs and args else '', kwargs) + sig = '{}({}{}{})'.format(name, args, ', ' if kwargs and args else '', kwargs) if isinstance(result, _Binds): - yield '%s = %s\n' % (result.value, sig) + yield f'{result.value} = {sig}\n' elif isinstance(result, _Returns): if binding is None: - yield '%s == %s # returns\n' % (sig, result.value) + yield f'{sig} == {result.value} # returns\n' else: - yield '%s.%s == %s # returns\n' % (binding, sig, result.value) + yield f'{binding}.{sig} == {result.value} # returns\n' elif isinstance(result, _Raises): if binding is None: - yield '%s ** %s # raises\n' % (sig, result.value) + yield f'{sig} ** {result.value} # raises\n' else: - yield '%s.%s ** %s # raises\n' % (binding, sig, result.value) + yield f'{binding}.{sig} ** {result.value} # raises\n' diff --git a/src/aspectlib/utils.py b/src/aspectlib/utils.py index c936911..fea87e6 100644 --- a/src/aspectlib/utils.py +++ b/src/aspectlib/utils.py @@ -7,7 +7,7 @@ from functools import wraps from inspect import isclass -RegexType = type(re.compile("")) +RegexType = type(re.compile('')) PY3 = sys.version_info[0] == 3 PY310 = PY3 and sys.version_info[1] >= 10 @@ -50,7 +50,7 @@ def camelcase_to_underscores(name): def qualname(obj): if hasattr(obj, '__module__') and obj.__module__ not in ('builtins', 'exceptions'): - return '%s.%s' % (obj.__module__, obj.__name__) + return f'{obj.__module__}.{obj.__name__}' else: return obj.__name__ @@ -72,19 +72,19 @@ def make_method_matcher(regex_or_regexstr_or_namelist): elif isinstance(regex_or_regexstr_or_namelist, RegexType): return regex_or_regexstr_or_namelist.match else: - raise TypeError("Unacceptable methods spec %r." % regex_or_regexstr_or_namelist) + raise TypeError(f'Unacceptable methods spec {regex_or_regexstr_or_namelist!r}.') -class Sentinel(object): +class Sentinel: def __init__(self, name, doc=''): self.name = name self.__doc__ = doc def __repr__(self): if not self.__doc__: - return "%s" % self.name + return f'{self.name}' else: - return "%s: %s" % (self.name, self.__doc__) + return f'{self.name}: {self.__doc__}' __str__ = __repr__ @@ -99,8 +99,8 @@ def __init__(self, value): { '__slots__': 'value', '__init__': __init__, - '__str__': lambda self: "%s(%s)" % (name, self.value), - '__repr__': lambda self: "%s(%r)" % (name, self.value), + '__str__': lambda self: f'{name}({self.value})', + '__repr__': lambda self: f'{name}({self.value!r})', '__eq__': lambda self, other: type(self) is type(other) and self.value == other.value, }, ) @@ -123,13 +123,14 @@ def mimic(wrapper, func, module=None): representers = { - tuple: lambda obj, aliases: "(%s%s)" % (', '.join(repr_ex(i) for i in obj), ',' if len(obj) == 1 else ''), - list: lambda obj, aliases: "[%s]" % ', '.join(repr_ex(i) for i in obj), - set: lambda obj, aliases: "set([%s])" % ', '.join(repr_ex(i) for i in obj), - frozenset: lambda obj, aliases: "set([%s])" % ', '.join(repr_ex(i) for i in obj), - deque: lambda obj, aliases: "collections.deque([%s])" % ', '.join(repr_ex(i) for i in obj), - dict: lambda obj, aliases: "{%s}" - % ', '.join("%s: %s" % (repr_ex(k), repr_ex(v)) for k, v in (obj.items() if PY3 else obj.iteritems())), + tuple: lambda obj, aliases: '({}{})'.format(', '.join(repr_ex(i) for i in obj), ',' if len(obj) == 1 else ''), + list: lambda obj, aliases: '[{}]'.format(', '.join(repr_ex(i) for i in obj)), + set: lambda obj, aliases: 'set([{}])'.format(', '.join(repr_ex(i) for i in obj)), + frozenset: lambda obj, aliases: 'set([{}])'.format(', '.join(repr_ex(i) for i in obj)), + deque: lambda obj, aliases: 'collections.deque([{}])'.format(', '.join(repr_ex(i) for i in obj)), + dict: lambda obj, aliases: '{{{}}}'.format( + ', '.join(f'{repr_ex(k)}: {repr_ex(v)}' for k, v in (obj.items() if PY3 else obj.iteritems())) + ), } @@ -137,7 +138,7 @@ def _make_fixups(): for obj in ('os.stat_result', 'grp.struct_group', 'pwd.struct_passwd'): mod, attr = obj.split('.') try: - yield getattr(__import__(mod), attr), lambda obj, aliases, prefix=obj: "%s(%r)" % (prefix, obj.__reduce__()[1][0]) + yield getattr(__import__(mod), attr), lambda obj, aliases, prefix=obj: f'{prefix}({obj.__reduce__()[1][0]!r})' except ImportError: continue @@ -148,7 +149,7 @@ def _make_fixups(): def repr_ex(obj, aliases=()): kind, ident = type(obj), id(obj) if isinstance(kind, BaseException): - return "%s(%s)" % (qualname(type(obj)), ', '.join(repr_ex(i, aliases) for i in obj.args)) + return '{}({})'.format(qualname(type(obj)), ', '.join(repr_ex(i, aliases) for i in obj.args)) elif isclass(obj): return qualname(obj) elif kind in representers: diff --git a/tests/mymod.py b/tests/mymod.py index 9855e10..eb019b8 100644 --- a/tests/mymod.py +++ b/tests/mymod.py @@ -1,6 +1,6 @@ def func(arg): - print("Got", arg, "in the real code!") + print('Got', arg, 'in the real code!') def badfunc(): - raise ValueError("boom!") + raise ValueError('boom!') diff --git a/tests/test_aspectlib.py b/tests/test_aspectlib.py index a50558f..ade88b4 100644 --- a/tests/test_aspectlib.py +++ b/tests/test_aspectlib.py @@ -1,12 +1,11 @@ -# encoding: utf8 -from pytest import raises +import pytest import aspectlib from aspectlib.test import mock from aspectlib.test import record -class Base(object): +class Base: def meth(*_): return 'base' @@ -44,7 +43,7 @@ def module_func2(): module_func3 = module_func2 -class NormalTestClass(object): +class NormalTestClass: some = 'attribute' def __init__(self, foo=None): @@ -156,7 +155,7 @@ def static_foobar(foo, bar=None): return 'subsub' + (bar or foo) -class SlotsTestClass(object): +class SlotsTestClass: __slots__ = 'inst', 'klass', 'static', 'other', 'foo', 'bar' some = 'attribute' @@ -220,7 +219,7 @@ def aspect(): yield def aspect_fail(): - return "crap" + return 'crap' aspect.advising_function = aspect_fail @@ -228,7 +227,7 @@ def aspect_fail(): def func(): pass - raises(aspectlib.ExpectedGenerator, func) + pytest.raises(aspectlib.ExpectedGenerator, func) def test_aspect_gen_bind(): @@ -271,7 +270,7 @@ def aspect(): yield def aspect_fail(): - return "crap" + return 'crap' aspect.advising_function = aspect_fail @@ -279,14 +278,14 @@ def aspect_fail(): def func(): yield - raises(aspectlib.ExpectedGenerator, list, func()) + pytest.raises(aspectlib.ExpectedGenerator, list, func()) def test_aspect_bad_decorate(): def aspect(): - return "crap" + return 'crap' - raises(aspectlib.ExpectedGeneratorFunction, aspectlib.Aspect, aspect) + pytest.raises(aspectlib.ExpectedGeneratorFunction, aspectlib.Aspect, aspect) def test_aspect_return(): @@ -327,7 +326,7 @@ def aspect(): @aspect def func(): - 1 / 0 + 1 / 0 # noqa: B018 assert func() == 'stuff' @@ -335,14 +334,14 @@ def func(): def test_aspect_raise_from_aspect(): @aspectlib.Aspect def aspect(): - 1 / 0 + 1 / 0 # noqa: B018 yield @aspect def func(): pass - raises(ZeroDivisionError, func) + pytest.raises(ZeroDivisionError, func) def test_aspect_return_but_call(): @@ -371,28 +370,28 @@ def test_weave_func(): def test_broken_aspect(): - raises(aspectlib.ExpectedAdvice, aspectlib.weave, None, None) + pytest.raises(aspectlib.ExpectedAdvice, aspectlib.weave, None, None) def test_weave_empty_target(): - raises(aspectlib.ExpectedAdvice, aspectlib.weave, (), None) + pytest.raises(aspectlib.ExpectedAdvice, aspectlib.weave, (), None) def test_weave_missing_global(cls=Global): global Global Global = 'crap' try: - raises(AssertionError, aspectlib.weave, cls, mock('stuff'), lazy=True) + pytest.raises(AssertionError, aspectlib.weave, cls, mock('stuff'), lazy=True) finally: Global = cls def test_weave_str_missing_target(): - raises(AttributeError, aspectlib.weave, 'test_pkg1.test_pkg2.target', mock('foobar')) + pytest.raises(AttributeError, aspectlib.weave, 'test_pkg1.test_pkg2.target', mock('foobar')) def test_weave_str_bad_target(): - raises(TypeError, aspectlib.weave, 'test_pkg1.test_pkg2.test_mod.a', mock('foobar')) + pytest.raises(TypeError, aspectlib.weave, 'test_pkg1.test_pkg2.test_mod.a', mock('foobar')) def test_weave_str_target(): @@ -446,7 +445,7 @@ def test_weave_wrong_module(): None, ( "Setting test_aspectlib.MissingGlobal to . " - "There was no previous definition, probably patching the wrong module.", + 'There was no previous definition, probably patching the wrong module.', ), {}, ) @@ -501,7 +500,7 @@ def test_weave_bad_args4(): def test_weave_bad_args5(): - raises(TypeError, aspectlib.weave, Sub, mock('stuff'), methods=False) + pytest.raises(TypeError, aspectlib.weave, Sub, mock('stuff'), methods=False) def test_weave_class_meth(): @@ -570,7 +569,7 @@ def test_weave_subclass_meth_from_baseclass(): @aspectlib.Aspect def aspect(*args): result = yield - history.append(args + (result,)) + history.append((*args, result)) yield aspectlib.Return('bar-' + result) with aspectlib.weave(NormalTestSubClass.only_in_base, aspect): @@ -590,7 +589,7 @@ def test_weave_subclass_meth_from_baseclass_2_level(): @aspectlib.Aspect def aspect(*args): result = yield - history.append(args + (result,)) + history.append((*args, result)) yield aspectlib.Return('bar-' + result) with aspectlib.weave(NormalTestSubSubClass.only_in_base, aspect): @@ -610,7 +609,7 @@ def test_weave_legacy_subclass_meth_from_baseclass(): @aspectlib.Aspect def aspect(*args): result = yield - history.append(args + (result,)) + history.append((*args, result)) yield aspectlib.Return('bar-' + result) with aspectlib.weave(LegacyTestSubClass.only_in_base, aspect): @@ -630,7 +629,7 @@ def test_weave_legacy_subclass_meth_from_baseclass_2_level(): @aspectlib.Aspect def aspect(*args): result = yield - history.append(args + (result,)) + history.append((*args, result)) yield aspectlib.Return('bar-' + result) with aspectlib.weave(LegacyTestSubSubClass.only_in_base, aspect): @@ -1147,9 +1146,9 @@ def aspect(): @aspect def func(): - 1 / 0 + 1 / 0 # noqa: B018 - raises(ZeroDivisionError, func) + pytest.raises(ZeroDivisionError, func) def test_weave_unknown(): @@ -1157,7 +1156,7 @@ def test_weave_unknown(): def aspect(): yield aspectlib.Proceed - raises(aspectlib.UnsupportedType, aspectlib.weave, 1, aspect) + pytest.raises(aspectlib.UnsupportedType, aspectlib.weave, 1, aspect) def test_weave_unimportable(): @@ -1165,7 +1164,7 @@ def test_weave_unimportable(): def aspect(): yield aspectlib.Proceed - raises(ImportError, aspectlib.weave, "asdf1.qwer2", aspect) + pytest.raises(ImportError, aspectlib.weave, 'asdf1.qwer2', aspect) def test_weave_subclass(Bub=Sub): @@ -1211,22 +1210,22 @@ def _internal(): pass -def ăbc(): +def ăbc(): # noqa: PLC2401 pass -def test_ăbc(): +def test_ăbc(): # noqa: PLC2401 with aspectlib.weave('test_aspectlib.ăbc', mock('stuff')): assert ăbc() == 'stuff' def test_invalid_string_target(): - raises(SyntaxError, aspectlib.weave, 'inva lid', mock(None)) - raises(SyntaxError, aspectlib.weave, 'os.inva lid', mock(None)) - raises(SyntaxError, aspectlib.weave, 'os.2invalid', mock(None)) - raises(SyntaxError, aspectlib.weave, 'os.some,junk', mock(None)) - raises(SyntaxError, aspectlib.weave, 'os.some?junk', mock(None)) - raises(SyntaxError, aspectlib.weave, 'os.some*junk', mock(None)) + pytest.raises(SyntaxError, aspectlib.weave, 'inva lid', mock(None)) + pytest.raises(SyntaxError, aspectlib.weave, 'os.inva lid', mock(None)) + pytest.raises(SyntaxError, aspectlib.weave, 'os.2invalid', mock(None)) + pytest.raises(SyntaxError, aspectlib.weave, 'os.some,junk', mock(None)) + pytest.raises(SyntaxError, aspectlib.weave, 'os.some?junk', mock(None)) + pytest.raises(SyntaxError, aspectlib.weave, 'os.some*junk', mock(None)) with aspectlib.weave('test_aspectlib._internal', mock('stuff')): assert _internal() == 'stuff' @@ -1238,15 +1237,15 @@ def test_list_of_aspects(): assert module_func.calls == [(None, (1, 2, 3), {})] with aspectlib.weave(module_func, [mock('foobar', call=True), record]): - raises(TypeError, module_func, 1, 2, 3) + pytest.raises(TypeError, module_func, 1, 2, 3) assert module_func.calls == [(None, (1, 2, 3), {})] def test_list_of_invalid_aspects(): - raises(AssertionError, aspectlib.weave, module_func, [lambda func: None]) - raises(TypeError, aspectlib.weave, module_func, [lambda: None]) - raises(aspectlib.ExpectedAdvice, aspectlib.weave, module_func, [None]) - raises(aspectlib.ExpectedAdvice, aspectlib.weave, module_func, ['foobar']) + pytest.raises(AssertionError, aspectlib.weave, module_func, [lambda func: None]) + pytest.raises(TypeError, aspectlib.weave, module_func, [lambda: None]) + pytest.raises(aspectlib.ExpectedAdvice, aspectlib.weave, module_func, [None]) + pytest.raises(aspectlib.ExpectedAdvice, aspectlib.weave, module_func, ['foobar']) def test_aspect_on_func(): @@ -1272,7 +1271,7 @@ def aspect(): @aspect def func(): - raise RuntimeError() + raise RuntimeError assert func() == 'squelched' assert hist == ['before', 'error', 'finally', 'closed'] @@ -1281,13 +1280,13 @@ def func(): def test_aspect_on_func_invalid_advice(): @aspectlib.Aspect def aspect(): - yield "stuff" + yield 'stuff' @aspect def func(): - raise RuntimeError() + raise RuntimeError - raises(aspectlib.UnacceptableAdvice, func) + pytest.raises(aspectlib.UnacceptableAdvice, func) def test_aspect_on_generator_func(): @@ -1314,9 +1313,8 @@ def aspect(): @aspect def func(): - for i in range(3): - yield i - raise RuntimeError() + yield from range(3) + raise RuntimeError assert list(func()) == [0, 1, 2] print(hist) @@ -1330,11 +1328,10 @@ def aspect(): @aspect def func(): - for i in range(3): - yield i - raise RuntimeError() + yield from range(3) + raise RuntimeError - raises(aspectlib.UnacceptableAdvice, list, func()) + pytest.raises(aspectlib.UnacceptableAdvice, list, func()) def test_aspect_on_generator_different_args(): @@ -1416,7 +1413,7 @@ def func(): gen = func() print(next(gen)) - raises(StopIteration, gen.throw, RuntimeError) + pytest.raises(StopIteration, gen.throw, RuntimeError) assert excs == [RuntimeError] @@ -1455,7 +1452,7 @@ def test_weave_module(strmod=None): def test_weave_module_as_str(): - test_weave_module("test_pkg1.test_pkg2.test_mod") + test_weave_module('test_pkg1.test_pkg2.test_mod') def test_weave_method(): @@ -1467,7 +1464,7 @@ def intercept(func, *args): intercepted.append(args) yield aspectlib.Proceed(*args) - class Foo(object): + class Foo: def foo(self, arg): calls.append((self, arg)) @@ -1488,7 +1485,7 @@ def intercept(func, *args): intercepted.append(args) yield - class Foo(object): + class Foo: def foo(self, arg): calls.append((self, arg)) diff --git a/tests/test_aspectlib_debug.py b/tests/test_aspectlib_debug.py index 4d91c46..976719c 100644 --- a/tests/test_aspectlib_debug.py +++ b/tests/test_aspectlib_debug.py @@ -14,10 +14,10 @@ from io import StringIO LOG_TEST_SIMPLE = ( - r'''^some_meth\(1, 2, 3, a=4\) +<<< .*tests/test_aspectlib_debug.py:\d+:test_simple.* -some_meth => \.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\. !"#\$%&\'\(\)\*\+,-\./0123456789:;<=>\?@''' - r'''ABCDEFGHIJKLMNOPQRSTUVWXYZ\[\\\]\^_`abcdefghijklmnopqrstuvwxyz\{\|\}~\.+ -$''' + r"""^some_meth\(1, 2, 3, a=4\) +<<< .*tests/test_aspectlib_debug.py:\d+:test_simple.* +some_meth => \.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\.\. !"#\$%&\'\(\)\*\+,-\./0123456789:;<=>\?@""" + r"""ABCDEFGHIJKLMNOPQRSTUVWXYZ\[\\\]\^_`abcdefghijklmnopqrstuvwxyz\{\|\}~\.+ +$""" ) @@ -25,7 +25,7 @@ def some_meth(*_args, **_kwargs): return ''.join(chr(i) for i in range(255)) -class MyStuff(object): +class MyStuff: def __init__(self, foo): self.foo = foo @@ -58,7 +58,7 @@ def test_simple(): def test_fail_to_log(): - @aspectlib.debug.log(print_to="crap") + @aspectlib.debug.log(print_to='crap') def foo(): pass @@ -145,7 +145,7 @@ def test_no_stack_old_style(): ) -@pytest.mark.skipif(sys.version_info < (2, 7), reason="No weakref.WeakSet on Python<=2.6") +@pytest.mark.skipif(sys.version_info < (2, 7), reason='No weakref.WeakSet on Python<=2.6') def test_weakref(): with aspectlib.weave(MyStuff, aspectlib.debug.log): s = weakref.WeakSet() @@ -154,7 +154,7 @@ def test_weakref(): print(list(s)) -@pytest.mark.skipif(sys.version_info < (2, 7), reason="No weakref.WeakSet on Python<=2.6") +@pytest.mark.skipif(sys.version_info < (2, 7), reason='No weakref.WeakSet on Python<=2.6') def test_weakref_oldstyle(): with aspectlib.weave(OldStuff, aspectlib.debug.log): s = weakref.WeakSet() diff --git a/tests/test_aspectlib_py3.py b/tests/test_aspectlib_py3.py index 40c056b..dc0ed3b 100644 --- a/tests/test_aspectlib_py3.py +++ b/tests/test_aspectlib_py3.py @@ -1,6 +1,3 @@ -# encoding: utf8 - - import pytest import aspectlib diff --git a/tests/test_aspectlib_py37.py b/tests/test_aspectlib_py37.py index 96b112b..daf10e4 100644 --- a/tests/test_aspectlib_py37.py +++ b/tests/test_aspectlib_py37.py @@ -1,10 +1,6 @@ -# encoding: utf8 - - import pytest import aspectlib - from test_aspectlib_py3 import consume @@ -50,10 +46,10 @@ def aspect(): def func(): val = 99 for _ in range(3): - print("YIELD", val + 1) + print('YIELD', val + 1) val = yield val + 1 - print("GOT", val) - return "the-return-value" + print('GOT', val) + return 'the-return-value' gen = func() data = [] @@ -136,9 +132,9 @@ def func(): while 1: next(gen) except StopIteration as exc: - assert exc.args == ('result',) + assert exc.args == ('result',) # noqa: PT017 else: - raise AssertionError("did not raise StopIteration") + raise AssertionError('did not raise StopIteration') def test_aspect_chain_on_generator_no_return(): diff --git a/tests/test_aspectlib_test.py b/tests/test_aspectlib_test.py index 16c8e9c..02514ce 100644 --- a/tests/test_aspectlib_test.py +++ b/tests/test_aspectlib_test.py @@ -1,5 +1,4 @@ -from pytest import raises -from test_pkg1.test_pkg2 import test_mod +import pytest from aspectlib.test import OrderedDict from aspectlib.test import Story @@ -12,6 +11,7 @@ from aspectlib.test import record from aspectlib.utils import PY310 from aspectlib.utils import repr_ex +from test_pkg1.test_pkg2 import test_mod pytest_plugins = ('pytester',) @@ -64,7 +64,7 @@ def test_record_result(): def test_record_exception(): fun = record(results=True)(rfun) - raises(RuntimeError, fun) + pytest.raises(RuntimeError, fun) assert fun.calls == [ (None, (), {}, None, exc), ] @@ -88,7 +88,7 @@ def test_record_exception_callback(): fun = record(results=True, callback=lambda *args: calls.append(args))(rfun) - raises(RuntimeError, fun) + pytest.raises(RuntimeError, fun) assert calls == [ (None, 'test_aspectlib_test.rfun', (), {}, None, exc), ] @@ -152,23 +152,23 @@ def test_record_as_context(): def test_bad_mock(): - raises(TypeError, mock) - raises(TypeError, mock, call=False) + pytest.raises(TypeError, mock) + pytest.raises(TypeError, mock, call=False) def test_simple_mock(): - assert "foobar" == mock("foobar")(module_fun)(1) + assert 'foobar' == mock('foobar')(module_fun)(1) def test_mock_no_calls(): with record(module_fun) as history: - assert "foobar" == mock("foobar")(module_fun)(2) + assert 'foobar' == mock('foobar')(module_fun)(2) assert history.calls == [] def test_mock_with_calls(): with record(module_fun) as history: - assert "foobar" == mock("foobar", call=True)(module_fun)(3) + assert 'foobar' == mock('foobar', call=True)(module_fun)(3) assert history.calls == [(None, (3,), {})] @@ -193,7 +193,7 @@ def test_double_recording(): def test_record_not_iscalled_and_results(): - raises(AssertionError, record, module_fun, iscalled=False, results=True) + pytest.raises(AssertionError, record, module_fun, iscalled=False, results=True) record(module_fun, iscalled=False, results=False) record(module_fun, iscalled=True, results=True) record(module_fun, iscalled=True, results=False) @@ -201,23 +201,23 @@ def test_record_not_iscalled_and_results(): def test_story_empty_play_noproxy(): with Story(test_mod).replay(recurse_lock=True, proxy=False, strict=False) as replay: - raises(AssertionError, test_mod.target) + pytest.raises(AssertionError, test_mod.target) assert replay._actual == {} def test_story_empty_play_proxy(): assert test_mod.target() is None - raises(TypeError, test_mod.target, 123) + pytest.raises(TypeError, test_mod.target, 123) with Story(test_mod).replay(recurse_lock=True, proxy=True, strict=False) as replay: assert test_mod.target() is None - raises(TypeError, test_mod.target, 123) + pytest.raises(TypeError, test_mod.target, 123) assert format_calls(replay._actual) == format_calls( OrderedDict( [ - ((None, 'test_pkg1.test_pkg2.test_mod.target', '', ''), _Returns("None")), + ((None, 'test_pkg1.test_pkg2.test_mod.target', '', ''), _Returns('None')), ( (None, 'test_pkg1.test_pkg2.test_mod.target', '123', ''), _Raises( @@ -235,14 +235,14 @@ def test_story_empty_play_proxy(): def test_story_empty_play_noproxy_class(): with Story(test_mod).replay(recurse_lock=True, proxy=False, strict=False) as replay: - raises(AssertionError, test_mod.Stuff, 1, 2) + pytest.raises(AssertionError, test_mod.Stuff, 1, 2) assert replay._actual == {} def test_story_empty_play_error_on_init(): with Story(test_mod).replay(strict=False) as replay: - raises(ValueError, test_mod.Stuff, "error") + pytest.raises(ValueError, test_mod.Stuff, 'error') # noqa: PT011 print(replay._actual) assert replay._actual == OrderedDict([((None, 'test_pkg1.test_pkg2.test_mod.Stuff', "'error'", ''), _Raises('ValueError()'))]) @@ -253,21 +253,21 @@ def test_story_half_play_noproxy_class(): with story.replay(recurse_lock=True, proxy=False, strict=False): obj = test_mod.Stuff(1, 2) - raises(AssertionError, obj.mix, 3, 4) + pytest.raises(AssertionError, obj.mix, 3, 4) def test_xxx(): with Story(test_mod) as story: obj = test_mod.Stuff(1, 2) - test_mod.target(1) == 2 - test_mod.target(2) == 3 + test_mod.target(1) == 2 # noqa: B015 + test_mod.target(2) == 3 # noqa: B015 test_mod.target(3) ** ValueError other = test_mod.Stuff(2, 2) - obj.other('a') == other - obj.meth('a') == 'x' + obj.other('a') == other # noqa: B015 + obj.meth('a') == 'x' # noqa: B015 obj = test_mod.Stuff(2, 3) obj.meth() ** ValueError('crappo') - obj.meth('c') == 'x' + obj.meth('c') == 'x' # noqa: B015 with story.replay(recurse_lock=True, strict=False) as replay: obj = test_mod.Stuff(1, 2) @@ -280,10 +280,10 @@ def test_xxx(): obj.meth() for k, v in story._calls.items(): - print(k, "=>", v) - print("############## UNEXPECTED ##############") + print(k, '=>', v) + print('############## UNEXPECTED ##############') for k, v in replay._actual.items(): - print(k, "=>", v) + print(k, '=>', v) # TODO @@ -291,12 +291,12 @@ def test_xxx(): def test_story_text_helpers(): with Story(test_mod) as story: obj = test_mod.Stuff(1, 2) - obj.meth('a') == 'x' - obj.meth('b') == 'y' + obj.meth('a') == 'x' # noqa: B015 + obj.meth('b') == 'y' # noqa: B015 obj = test_mod.Stuff(2, 3) - obj.meth('c') == 'z' - test_mod.target(1) == 2 - test_mod.target(2) == 3 + obj.meth('c') == 'z' # noqa: B015 + test_mod.target(1) == 2 # noqa: B015 + test_mod.target(2) == 3 # noqa: B015 with story.replay(recurse_lock=True, strict=False) as replay: obj = test_mod.Stuff(1, 2) @@ -351,13 +351,13 @@ def test_story_empty_play_proxy_class_missing_report(LineMatcher): obj = test_mod.Stuff(1, 2) obj.mix(3, 4) obj.mix('a', 'b') - raises(ValueError, obj.raises, 123) + pytest.raises(ValueError, obj.raises, 123) # noqa: PT011 obj = test_mod.Stuff(0, 1) obj.mix('a', 'b') obj.mix(3, 4) test_mod.target() - raises(ValueError, test_mod.raises, 'badarg') - raises(ValueError, obj.raises, 123) + pytest.raises(ValueError, test_mod.raises, 'badarg') # noqa: PT011 + pytest.raises(ValueError, obj.raises, 123) # noqa: PT011 test_mod.ThatLONGStuf(1).mix(2) test_mod.ThatLONGStuf(3).mix(4) obj = test_mod.ThatLONGStuf(2) @@ -366,27 +366,27 @@ def test_story_empty_play_proxy_class_missing_report(LineMatcher): obj.mix(10) LineMatcher(replay.diff.splitlines()).fnmatch_lines( [ - "--- expected", - "+++ actual", - "@@ -0,0 +1,18 @@", - "+stuff_1 = test_pkg1.test_pkg2.test_mod.Stuff(1, 2)", - "+stuff_1.mix(3, 4) == (1, 2, 3, 4) # returns", + '--- expected', + '+++ actual', + '@@ -0,0 +1,18 @@', + '+stuff_1 = test_pkg1.test_pkg2.test_mod.Stuff(1, 2)', + '+stuff_1.mix(3, 4) == (1, 2, 3, 4) # returns', "+stuff_1.mix('a', 'b') == (1, 2, 'a', 'b') # returns", - "+stuff_1.raises(123) ** ValueError((123,)*) # raises", - "+stuff_2 = test_pkg1.test_pkg2.test_mod.Stuff(0, 1)", + '+stuff_1.raises(123) ** ValueError((123,)*) # raises', + '+stuff_2 = test_pkg1.test_pkg2.test_mod.Stuff(0, 1)', "+stuff_2.mix('a', 'b') == (0, 1, 'a', 'b') # returns", - "+stuff_2.mix(3, 4) == (0, 1, 3, 4) # returns", - "+test_pkg1.test_pkg2.test_mod.target() == None # returns", + '+stuff_2.mix(3, 4) == (0, 1, 3, 4) # returns', + '+test_pkg1.test_pkg2.test_mod.target() == None # returns', "+test_pkg1.test_pkg2.test_mod.raises('badarg') ** ValueError(('badarg',)*) # raises", - "+stuff_2.raises(123) ** ValueError((123,)*) # raises", - "+that_long_stuf_1 = test_pkg1.test_pkg2.test_mod.ThatLONGStuf(1)", - "+that_long_stuf_1.mix(2) == (1, 2) # returns", - "+that_long_stuf_2 = test_pkg1.test_pkg2.test_mod.ThatLONGStuf(3)", - "+that_long_stuf_2.mix(4) == (3, 4) # returns", - "+that_long_stuf_3 = test_pkg1.test_pkg2.test_mod.ThatLONGStuf(2)", - "+that_long_stuf_3.mix() == (2,) # returns", - "+that_long_stuf_3.meth() == None # returns", - "+that_long_stuf_3.mix(10) == (2, 10) # returns", + '+stuff_2.raises(123) ** ValueError((123,)*) # raises', + '+that_long_stuf_1 = test_pkg1.test_pkg2.test_mod.ThatLONGStuf(1)', + '+that_long_stuf_1.mix(2) == (1, 2) # returns', + '+that_long_stuf_2 = test_pkg1.test_pkg2.test_mod.ThatLONGStuf(3)', + '+that_long_stuf_2.mix(4) == (3, 4) # returns', + '+that_long_stuf_3 = test_pkg1.test_pkg2.test_mod.ThatLONGStuf(2)', + '+that_long_stuf_3.mix() == (2,) # returns', + '+that_long_stuf_3.meth() == None # returns', + '+that_long_stuf_3.mix(10) == (2, 10) # returns', ] ) @@ -399,22 +399,22 @@ def test_story_empty_play_proxy_class(): assert obj.mix(3, 4) == (1, 2, 3, 4) assert obj.mix('a', 'b') == (1, 2, 'a', 'b') - raises(TypeError, obj.meth, 123) + pytest.raises(TypeError, obj.meth, 123) obj = test_mod.Stuff(0, 1) assert obj.mix('a', 'b') == (0, 1, 'a', 'b') assert obj.mix(3, 4) == (0, 1, 3, 4) - raises(TypeError, obj.meth, 123) + pytest.raises(TypeError, obj.meth, 123) assert format_calls(replay._actual) == format_calls( OrderedDict( [ - ((None, 'test_pkg1.test_pkg2.test_mod.Stuff', "1, 2", ''), _Binds('stuff_1')), - (('stuff_1', 'mix', "3, 4", ''), _Returns("(1, 2, 3, 4)")), + ((None, 'test_pkg1.test_pkg2.test_mod.Stuff', '1, 2', ''), _Binds('stuff_1')), + (('stuff_1', 'mix', '3, 4', ''), _Returns('(1, 2, 3, 4)')), (('stuff_1', 'mix', "'a', 'b'", ''), _Returns("(1, 2, 'a', 'b')")), ( - ('stuff_1', 'meth', "123", ''), + ('stuff_1', 'meth', '123', ''), _Raises( repr_ex( TypeError( @@ -425,11 +425,11 @@ def test_story_empty_play_proxy_class(): ) ), ), - ((None, 'test_pkg1.test_pkg2.test_mod.Stuff', "0, 1", ''), _Binds('stuff_2')), + ((None, 'test_pkg1.test_pkg2.test_mod.Stuff', '0, 1', ''), _Binds('stuff_2')), (('stuff_2', 'mix', "'a', 'b'", ''), _Returns("(0, 1, 'a', 'b')")), - (('stuff_2', 'mix', "3, 4", ''), _Returns("(0, 1, 3, 4)")), + (('stuff_2', 'mix', '3, 4', ''), _Returns('(0, 1, 3, 4)')), ( - ('stuff_2', 'meth', "123", ''), + ('stuff_2', 'meth', '123', ''), _Raises( repr_ex( TypeError( @@ -450,20 +450,20 @@ def test_story_half_play_proxy_class(): with Story(test_mod) as story: obj = test_mod.Stuff(1, 2) - obj.mix(3, 4) == (1, 2, 3, 4) + obj.mix(3, 4) == (1, 2, 3, 4) # noqa: B015 with story.replay(recurse_lock=True, proxy=True, strict=False) as replay: obj = test_mod.Stuff(1, 2) assert obj.mix(3, 4) == (1, 2, 3, 4) assert obj.meth() is None - raises(TypeError, obj.meth, 123) + pytest.raises(TypeError, obj.meth, 123) obj = test_mod.Stuff(0, 1) assert obj.mix('a', 'b') == (0, 1, 'a', 'b') assert obj.mix(3, 4) == (0, 1, 3, 4) - raises(TypeError, obj.meth, 123) + pytest.raises(TypeError, obj.meth, 123) assert replay.unexpected == format_calls( OrderedDict( [ @@ -480,7 +480,7 @@ def test_story_half_play_proxy_class(): ) ), ), - ((None, 'test_pkg1.test_pkg2.test_mod.Stuff', '0, 1', ''), _Binds("stuff_2")), + ((None, 'test_pkg1.test_pkg2.test_mod.Stuff', '0, 1', ''), _Binds('stuff_2')), (('stuff_2', 'mix', "'a', 'b'", ''), _Returns("(0, 1, 'a', 'b')")), (('stuff_2', 'mix', '3, 4', ''), _Returns('(0, 1, 3, 4)')), ( @@ -502,45 +502,45 @@ def test_story_half_play_proxy_class(): def test_story_full_play_noproxy(): with Story(test_mod) as story: - test_mod.target(123) == 'foobar' + test_mod.target(123) == 'foobar' # noqa: B015 test_mod.target(1234) ** ValueError with story.replay(recurse_lock=True, proxy=False, strict=False, dump=False) as replay: - raises(AssertionError, test_mod.target) + pytest.raises(AssertionError, test_mod.target) assert test_mod.target(123) == 'foobar' - raises(ValueError, test_mod.target, 1234) + pytest.raises(ValueError, test_mod.target, 1234) # noqa: PT011 - assert replay.unexpected == "" + assert replay.unexpected == '' def test_story_full_play_noproxy_dump(): with Story(test_mod) as story: - test_mod.target(123) == 'foobar' + test_mod.target(123) == 'foobar' # noqa: B015 test_mod.target(1234) ** ValueError with story.replay(recurse_lock=True, proxy=False, strict=False, dump=True) as replay: - raises(AssertionError, test_mod.target) + pytest.raises(AssertionError, test_mod.target) assert test_mod.target(123) == 'foobar' - raises(ValueError, test_mod.target, 1234) + pytest.raises(ValueError, test_mod.target, 1234) # noqa: PT011 - assert replay.unexpected == "" + assert replay.unexpected == '' def test_story_full_play_proxy(): with Story(test_mod) as story: - test_mod.target(123) == 'foobar' + test_mod.target(123) == 'foobar' # noqa: B015 test_mod.target(1234) ** ValueError with story.replay(recurse_lock=True, proxy=True, strict=False) as replay: assert test_mod.target() is None assert test_mod.target(123) == 'foobar' - raises(ValueError, test_mod.target, 1234) - raises(TypeError, test_mod.target, 'asdf') + pytest.raises(ValueError, test_mod.target, 1234) # noqa: PT011 + pytest.raises(TypeError, test_mod.target, 'asdf') assert replay.unexpected == format_calls( OrderedDict( [ - ((None, 'test_pkg1.test_pkg2.test_mod.target', '', ''), _Returns("None")), + ((None, 'test_pkg1.test_pkg2.test_mod.target', '', ''), _Returns('None')), ( (None, 'test_pkg1.test_pkg2.test_mod.target', "'asdf'", ''), _Raises( @@ -558,39 +558,39 @@ def test_story_full_play_proxy(): def test_story_result_wrapper(): x = StoryResultWrapper(lambda *a: None) - raises(AttributeError, setattr, x, 'stuff', 1) - raises(AttributeError, getattr, x, 'stuff') - raises(TypeError, lambda: x >> 2) - raises(TypeError, lambda: x << 1) - raises(TypeError, lambda: x > 1) - x == 1 + pytest.raises(AttributeError, setattr, x, 'stuff', 1) + pytest.raises(AttributeError, getattr, x, 'stuff') + pytest.raises(TypeError, lambda: x >> 2) + pytest.raises(TypeError, lambda: x << 1) + pytest.raises(TypeError, lambda: x > 1) + x == 1 # noqa: B015 x ** Exception() def test_story_result_wrapper_bad_exception(): x = StoryResultWrapper(lambda *a: None) - raises(RuntimeError, lambda: x**1) + pytest.raises(RuntimeError, lambda: x**1) x**Exception x ** Exception('boom!') def test_story_create(): with Story(test_mod) as story: - test_mod.target('a', 'b', 'c') == 'abc' + test_mod.target('a', 'b', 'c') == 'abc' # noqa: B015 test_mod.target() ** Exception - test_mod.target(1, 2, 3) == 'foobar' + test_mod.target(1, 2, 3) == 'foobar' # noqa: B015 obj = test_mod.Stuff('stuff') assert isinstance(obj, test_mod.Stuff) - obj.meth('other', 1, 2) == 123 - obj.mix('other') == 'mixymix' + obj.meth('other', 1, 2) == 123 # noqa: B015 + obj.mix('other') == 'mixymix' # noqa: B015 # from pprint import pprint as print # print (dict(story._calls)) assert dict(story._calls) == { (None, 'test_pkg1.test_pkg2.test_mod.Stuff', "'stuff'", ''): _Binds('stuff_1'), - ('stuff_1', 'meth', "'other', 1, 2", ''): _Returns("123"), + ('stuff_1', 'meth', "'other', 1, 2", ''): _Returns('123'), ('stuff_1', 'mix', "'other'", ''): _Returns("'mixymix'"), - (None, 'test_pkg1.test_pkg2.test_mod.target', '', ''): _Raises("Exception"), - (None, 'test_pkg1.test_pkg2.test_mod.target', "1, 2, 3", ''): _Returns("'foobar'"), + (None, 'test_pkg1.test_pkg2.test_mod.target', '', ''): _Raises('Exception'), + (None, 'test_pkg1.test_pkg2.test_mod.target', '1, 2, 3', ''): _Returns("'foobar'"), (None, 'test_pkg1.test_pkg2.test_mod.target', "'a', 'b', 'c'", ''): _Returns("'abc'"), } @@ -599,7 +599,7 @@ def xtest_story_empty_play_proxy_class_dependencies(): with Story(test_mod).replay(recurse_lock=True, proxy=True, strict=False) as replay: obj = test_mod.Stuff(1, 2) other = obj.other('x') - raises(ValueError, other.raises, 'badarg') + pytest.raises(ValueError, other.raises, 'badarg') # noqa: PT011 other.mix(3, 4) obj = test_mod.Stuff(0, 1) obj.mix(3, 4) @@ -608,4 +608,4 @@ def xtest_story_empty_play_proxy_class_dependencies(): print(repr(replay.diff)) - assert replay.diff == "" + assert replay.diff == '' diff --git a/tests/test_contrib.py b/tests/test_contrib.py index 5eb0373..3cc799b 100644 --- a/tests/test_contrib.py +++ b/tests/test_contrib.py @@ -32,11 +32,11 @@ def test_defaults(): def test_raises(): calls = [] - pytest.raises(OSError, retry(sleep=calls.append)(flaky_func), [None] * 6) + pytest.raises(OSError, retry(sleep=calls.append)(flaky_func), [None] * 6) # noqa: PT011 assert calls == [0, 0, 0, 0, 0] calls = [] - pytest.raises(OSError, retry(sleep=calls.append, retries=1)(flaky_func), [None, None]) + pytest.raises(OSError, retry(sleep=calls.append, retries=1)(flaky_func), [None, None]) # noqa: PT011 assert calls == [0] @@ -70,7 +70,7 @@ def test_backoff_flat(): def test_with_class(): logger = getLogger(__name__) - class Connection(object): + class Connection: count = 0 @retry @@ -81,24 +81,24 @@ def __init__(self, address): def __connect(self, *_, **__): self.count += 1 if self.count % 3: - raise OSError("Failed") + raise OSError('Failed') else: - logger.info("connected!") + logger.info('connected!') @retry(cleanup=__connect) def action(self, arg1, arg2): self.count += 1 if self.count % 3 == 0: - raise OSError("Failed") + raise OSError('Failed') else: - logger.info("action!") + logger.info('action!') def __repr__(self): - return "Connection@%s" % self.count + return f'Connection@{self.count}' with LogCapture([logger, contrib.logger]) as logcap: try: - conn = Connection("to-something") + conn = Connection('to-something') for i in range(5): conn.action(i, i) finally: diff --git a/tests/test_integrations.py b/tests/test_integrations.py index 08f6e13..7a8fc0b 100644 --- a/tests/test_integrations.py +++ b/tests/test_integrations.py @@ -28,9 +28,9 @@ def test_mock_builtin(): with aspectlib.weave(open, mock('foobar')): - assert open('???') == 'foobar' + assert open('???') == 'foobar' # noqa: PTH123 - assert open(__file__) != 'foobar' + assert open(__file__) != 'foobar' # noqa: PTH123 def test_mock_builtin_os(): @@ -43,11 +43,11 @@ def test_mock_builtin_os(): def test_record_warning(): with aspectlib.weave('warnings.warn', record): - warnings.warn('crap') + warnings.warn('crap', stacklevel=1) assert warnings.warn.calls == [(None, ('crap',), {})] -@pytest.mark.skipif(not hasattr(os, 'fork'), reason="os.fork not available") +@pytest.mark.skipif(not hasattr(os, 'fork'), reason='os.fork not available') def test_fork(): with aspectlib.weave('os.fork', mock('foobar')): pid = os.fork() @@ -67,7 +67,7 @@ def test_socket(target=socket.socket): s = socket.socket() try: s.connect(('127.0.0.1', 1)) - except Exception: + except Exception: # noqa: S110 pass print(buf.getvalue()) @@ -76,7 +76,7 @@ def test_socket(target=socket.socket): s = socket.socket() try: s.connect(('127.0.0.1', 1)) - except Exception: + except Exception: # noqa: S110 pass assert re.match(LOG_TEST_SOCKET, buf.getvalue()) @@ -108,10 +108,10 @@ def test_socket_all_methods(): with aspectlib.weave(socket.socket, aspectlib.debug.log(print_to=buf, stacktrace=False), lazy=True, methods=aspectlib.ALL_METHODS): socket.socket() - assert "}.__init__ => None" in buf.getvalue() + assert '}.__init__ => None' in buf.getvalue() -@pytest.mark.skipif(not hasattr(os, 'fork') or PYPY, reason="os.fork not available or PYPY") +@pytest.mark.skipif(not hasattr(os, 'fork') or PYPY, reason='os.fork not available or PYPY') def test_realsocket_makefile(): buf = StringIO() p = socket.socket() @@ -131,21 +131,21 @@ def test_realsocket_makefile(): s.settimeout(1) s.connect(p.getsockname()) fh = s.makefile('rwb', buffering=0) - fh.write(b"STUFF\n") + fh.write(b'STUFF\n') fh.readline() with dump_on_error(buf.getvalue): wait_for_strings( buf.getvalue, 0, - "}.connect", - "}.makefile", - "}.write(", - "}.send", - "}.write =>", - "}.readline()", - "}.recv", - "}.readline => ", + '}.connect', + '}.makefile', + '}.write(', + '}.send', + '}.write =>', + '}.readline()', + '}.recv', + '}.readline => ', ) else: try: @@ -161,7 +161,7 @@ def test_realsocket_makefile(): def test_weave_os_module(): calls = [] - with aspectlib.weave('os', record(calls=calls, extended=True), methods="getenv|walk"): + with aspectlib.weave('os', record(calls=calls, extended=True), methods='getenv|walk'): os.getenv('BUBU', 'bubu') os.walk('.') @@ -174,13 +174,13 @@ def test_decorate_asyncio_coroutine(): @debug.log(print_to=buf, module=False, stacktrace=2, result_repr=repr) async def coro(): await asyncio.sleep(0.01) - return "result" + return 'result' loop = asyncio.new_event_loop() loop.run_until_complete(coro()) output = buf.getvalue() print(output) - assert 'coro => %r' % 'result' in output + assert 'coro => {!r}'.format('result') in output def test_decorate_tornado_coroutine(): @@ -193,7 +193,7 @@ def coro(): yield gen.Task(loop.add_timeout, timedelta(microseconds=10)) else: yield gen.sleep(0.01) - return "result" + return 'result' asyncio_loop = asyncio.new_event_loop() try: @@ -204,4 +204,4 @@ def coro(): finally: asyncio.get_event_loop = get_event_loop output = buf.getvalue() - assert 'coro => %r' % 'result' in output + assert 'coro => {!r}'.format('result') in output diff --git a/tests/test_pkg1/test_pkg2/test_mod.py b/tests/test_pkg1/test_pkg2/test_mod.py index 729259a..4586d92 100644 --- a/tests/test_pkg1/test_pkg2/test_mod.py +++ b/tests/test_pkg1/test_pkg2/test_mod.py @@ -13,7 +13,7 @@ def raises(*a): a = 1 -class Stuff(object): +class Stuff: def __init__(self, *args): if args == ('error',): raise ValueError diff --git a/tests/test_pytestsupport.py b/tests/test_pytestsupport.py index c1bb6ad..ed3344c 100644 --- a/tests/test_pytestsupport.py +++ b/tests/test_pytestsupport.py @@ -1,7 +1,7 @@ from aspectlib import test -class Foo(object): +class Foo: def bar(self): return 1 From b85abdb0565d1598ce56bd49d49dc709d4e16081 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ionel=20Cristian=20M=C4=83rie=C8=99?= Date: Mon, 12 Aug 2024 18:36:13 +0300 Subject: [PATCH 175/179] Fix some cleanup regressions. --- tests/conftest.py | 7 ++++--- tests/test_aspectlib.py | 2 +- tests/test_integrations.py | 2 +- 3 files changed, 6 insertions(+), 5 deletions(-) diff --git a/tests/conftest.py b/tests/conftest.py index ee54d9d..29caa50 100644 --- a/tests/conftest.py +++ b/tests/conftest.py @@ -1,5 +1,6 @@ -def pytest_ignore_collect(path, config): - basename = path.basename +from pathlib import Path - if 'pytestsupport' in basename: + +def pytest_ignore_collect(collection_path: Path, config): + if 'pytestsupport' in collection_path.__fspath__(): return True diff --git a/tests/test_aspectlib.py b/tests/test_aspectlib.py index ade88b4..1c924f4 100644 --- a/tests/test_aspectlib.py +++ b/tests/test_aspectlib.py @@ -447,7 +447,7 @@ def test_weave_wrong_module(): "Setting test_aspectlib.MissingGlobal to . " 'There was no previous definition, probably patching the wrong module.', ), - {}, + {'stacklevel': 2}, ) ] diff --git a/tests/test_integrations.py b/tests/test_integrations.py index 7a8fc0b..7a7e143 100644 --- a/tests/test_integrations.py +++ b/tests/test_integrations.py @@ -44,7 +44,7 @@ def test_mock_builtin_os(): def test_record_warning(): with aspectlib.weave('warnings.warn', record): warnings.warn('crap', stacklevel=1) - assert warnings.warn.calls == [(None, ('crap',), {})] + assert warnings.warn.calls == [(None, ('crap',), {'stacklevel': 1})] @pytest.mark.skipif(not hasattr(os, 'fork'), reason='os.fork not available') From 2516ffe0110d93ac228f2fe15be74f905984f071 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ionel=20Cristian=20M=C4=83rie=C8=99?= Date: Mon, 12 Aug 2024 18:41:11 +0300 Subject: [PATCH 176/179] Ignore throw 3-arg deprecation. --- pytest.ini | 1 + 1 file changed, 1 insertion(+) diff --git a/pytest.ini b/pytest.ini index de53382..118418c 100644 --- a/pytest.ini +++ b/pytest.ini @@ -32,6 +32,7 @@ testpaths = filterwarnings = error ignore:Setting test_aspectlib.MissingGlobal to . There was no previous definition, probably patching the wrong module. + ignore:the \(type, exc, tb\) signature of throw\(\) is deprecated, use the single-arg signature instead.:DeprecationWarning # You can add exclusions, some examples: # ignore:'aspectlib' defines default_app_config:PendingDeprecationWarning:: # ignore:The {{% if::: From 8791387db7c310cd7a957bd7f86719c6485f37f1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ionel=20Cristian=20M=C4=83rie=C8=99?= Date: Mon, 12 Aug 2024 18:42:49 +0300 Subject: [PATCH 177/179] Add a tidelift security policy. --- SECURITY.rst | 5 +++++ 1 file changed, 5 insertions(+) create mode 100644 SECURITY.rst diff --git a/SECURITY.rst b/SECURITY.rst new file mode 100644 index 0000000..41127bf --- /dev/null +++ b/SECURITY.rst @@ -0,0 +1,5 @@ +Security contact information +============================ + +To report a security vulnerability, please use the `Tidelift security contact `_. +Tidelift will coordinate the fix and disclosure. From 671b0138d57a5c92ccbb56e22142278caf35d97f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ionel=20Cristian=20M=C4=83rie=C8=99?= Date: Mon, 12 Aug 2024 18:45:15 +0300 Subject: [PATCH 178/179] Add a tidelift security policy. --- SECURITY.md | 5 +++++ 1 file changed, 5 insertions(+) create mode 100644 SECURITY.md diff --git a/SECURITY.md b/SECURITY.md new file mode 100644 index 0000000..da9c516 --- /dev/null +++ b/SECURITY.md @@ -0,0 +1,5 @@ +## Security contact information + +To report a security vulnerability, please use the +[Tidelift security contact](https://tidelift.com/security). +Tidelift will coordinate the fix and disclosure. From 52092d5e8ee86705c0cad9f9b510dcedabbdc46f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ionel=20Cristian=20M=C4=83rie=C8=99?= Date: Mon, 12 Aug 2024 18:47:32 +0300 Subject: [PATCH 179/179] Wrong extension/format apparently. --- SECURITY.rst | 5 ----- 1 file changed, 5 deletions(-) delete mode 100644 SECURITY.rst diff --git a/SECURITY.rst b/SECURITY.rst deleted file mode 100644 index 41127bf..0000000 --- a/SECURITY.rst +++ /dev/null @@ -1,5 +0,0 @@ -Security contact information -============================ - -To report a security vulnerability, please use the `Tidelift security contact `_. -Tidelift will coordinate the fix and disclosure.