From f9591a43e5c3eab5eed2918d40af5773e15447c5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ionel=20Cristian=20M=C4=83rie=C8=99?= Date: Thu, 28 Jul 2016 17:26:41 +0300 Subject: [PATCH 01/16] Update README.rst --- README.rst | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/README.rst b/README.rst index bc4c60a..5621438 100644 --- a/README.rst +++ b/README.rst @@ -405,6 +405,12 @@ Testimonials -- `Isaac Dickinson `_ +.. + + It's so bad you had to write a pylint plugin :) + + -- Colin Dunklau on IRC (#python) + Apologies ========= From 0352f25ef5d26e883007a30b36a5d2084d90bb4e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ionel=20Cristian=20M=C4=83rie=C8=99?= Date: Sat, 13 Aug 2016 12:01:12 +0300 Subject: [PATCH 02/16] Address #4 --- README.rst | 27 +++++++++++++++++++++++---- 1 file changed, 23 insertions(+), 4 deletions(-) diff --git a/README.rst b/README.rst index 5621438..f47f5de 100644 --- a/README.rst +++ b/README.rst @@ -332,13 +332,32 @@ Worth noting: * attrs_ is faster because it doesn't allow your class to be used as a mixin (it doesn't do any ``super(cls, self).__init__(...)`` for you). -* the typical use-case doesn't allow you to have a custom ``__init__``. If you define a custom - ``__init__``, it will get overridden by the one attrs_ generates. +* The typical use-case doesn't allow you to have a custom ``__init__``. You can + use `@attr.s(init=False)` that will allow you to implement your own ``__init__``. + However, you can't have your own ``__init__`` that calls attrs_ provided ``__init__`` + (like in a subclassing scenario). * It works better with IDEs and source code analysis tools because of the attributes defined on the class. +* It's more composable if you only use `@attr.s` decorated hierarchies. Example: -All in all, attrs_ is a fast and minimal container library with no support for -subclasses. Definitely worth considering. + .. sourcecode:: pycon + + >>> @attr.s + ... class A(object): + ... a = attr.ib() + ... def get_a(self): + ... return self.a + >>> @attr.s + ... class B(object): + ... b = attr.ib() + >>> @attr.s + ... class C(B, A): + ... c = attr.ib() + >>> C(1, 2, 3) + C(a=1, b=2, c=3) + +All in all, attrs_ is a fast and minimal container library that does support subclasses, +but quite differently than `fields`. Definitely worth considering. .. _attrs: Date: Sat, 20 Aug 2016 14:18:42 +0300 Subject: [PATCH 03/16] Update skel. --- .cookiecutterrc | 66 +++++++++++++++++++-------------------- .coveragerc | 6 ++-- .editorconfig | 13 ++++++++ .gitignore | 1 + .travis.yml | 9 ++++-- AUTHORS.rst | 2 +- CONTRIBUTING.rst | 15 ++++----- LICENSE | 2 +- MANIFEST.in | 4 +-- README.rst | 19 ++++++------ ci/appveyor-bootstrap.py | 4 ++- ci/appveyor-download.py | 67 ++++++++++++++++++++-------------------- docs/conf.py | 23 ++++++++++---- docs/index.rst | 7 ++--- docs/readme.rst | 4 --- docs/requirements.txt | 3 +- setup.cfg | 3 -- setup.py | 11 ++++--- tox.ini | 12 +++---- 19 files changed, 146 insertions(+), 125 deletions(-) create mode 100644 .editorconfig mode change 100644 => 100755 ci/appveyor-download.py diff --git a/.cookiecutterrc b/.cookiecutterrc index 60d3ece..f450174 100644 --- a/.cookiecutterrc +++ b/.cookiecutterrc @@ -1,34 +1,34 @@ -# This file exists so you can easily regenerate your project. -# -# Unfortunately 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: 'fields' - email: 'contact@ionelmc.ro' - full_name: 'Ionel Cristian Mărieș' - github_username: 'ionelmc' - landscape: 'yes' - package_name: 'fields' - project_name: 'Fields' - project_short_description: 'Container class boilerplate killer.' - release_date: '2015-06-13' - repo_name: 'python-fields' - requiresio: 'yes' - scrutinizer: 'yes' - sphinx_theme: 'sphinx-py3doc-enhanced-theme' - test_matrix_configurator: 'no' - test_runner: 'pytest' - travis: 'yes' - version: '2.4.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: '-' + coveralls: 'yes' + distribution_name: fields + email: contact@ionelmc.ro + full_name: Ionel Cristian Mărieș + github_username: ionelmc + landscape: 'yes' + package_name: fields + project_name: Fields + project_short_description: Container class boilerplate killer. + release_date: '2016-04-13' + repo_name: python-fields + requiresio: 'yes' + scrutinizer: 'yes' + sphinx_doctest: 'no' + sphinx_theme: sphinx-py3doc-enhanced-theme + test_matrix_configurator: 'no' + test_matrix_separate_coverage: 'yes' + test_runner: pytest + travis: 'yes' + version: 5.0.0 + website: https://blog.ionelmc.ro + year: 2014-2016 diff --git a/.coveragerc b/.coveragerc index 077f08c..4af757e 100644 --- a/.coveragerc +++ b/.coveragerc @@ -2,8 +2,10 @@ source = src [run] -branch = True -source = src +branch = true +source = + src + tests parallel = true [report] 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 9e8fc16..a74475a 100644 --- a/.gitignore +++ b/.gitignore @@ -14,6 +14,7 @@ parts bin var sdist +wheelhouse develop-eggs .installed.cfg lib diff --git a/.travis.yml b/.travis.yml index ebdd015..ce023a6 100644 --- a/.travis.yml +++ b/.travis.yml @@ -3,7 +3,8 @@ 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 @@ -40,8 +41,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/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 diff --git a/CONTRIBUTING.rst b/CONTRIBUTING.rst index 354717d..36e7155 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-fields` for local development: -1. `Fork python-fields on GitHub `_. +1. Fork `python-fields `_ + (look for the "Fork" button). 2. Clone your fork locally:: git clone git@github.com:your_name_here/python-fields.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 4316757..92351c4 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 @@ -16,8 +16,6 @@ include CONTRIBUTING.rst include LICENSE include README.rst -include bootstrap.py -include conftest.py include tox.ini .travis.yml appveyor.yml global-exclude *.py[cod] __pycache__ *.so *.dylib diff --git a/README.rst b/README.rst index f47f5de..614975f 100644 --- a/README.rst +++ b/README.rst @@ -1,6 +1,8 @@ -====== -Fields -====== +======== +Overview +======== + +.. start-badges .. list-table:: :stub-columns: 1 @@ -49,6 +51,7 @@ Fields .. |codeclimate| image:: https://codeclimate.com/github/ionelmc/python-fields/badges/gpa.svg :target: https://codeclimate.com/github/ionelmc/python-fields :alt: CodeClimate Quality Status + .. |version| image:: https://img.shields.io/pypi/v/fields.svg?style=flat :alt: PyPI Package latest release :target: https://pypi.python.org/pypi/fields @@ -73,16 +76,12 @@ Fields :alt: Scrutinizer Status :target: https://scrutinizer-ci.com/g/ionelmc/python-fields/ -Container class boilerplate killer. -Features: +.. end-badges -* Human-readable ``__repr__`` -* Complete set of comparison methods -* Keyword and positional argument support. Works like a normal class - you can override just about anything in the - subclass (eg: a custom ``__init__``). In contrast, `hynek/characteristic `_ - forces different call schematics and calls your ``__init__`` with different arguments. +Container class boilerplate killer. +* Free software: BSD license Installation ============ diff --git a/ci/appveyor-bootstrap.py b/ci/appveyor-bootstrap.py index bc351bc..b411889 100644 --- a/ci/appveyor-bootstrap.py +++ b/ci/appveyor-bootstrap.py @@ -5,8 +5,10 @@ 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 CalledProcessError from subprocess import check_call try: @@ -71,7 +73,7 @@ def install_python(version, arch, home): print("Running:", " ".join(cmd)) try: check_call(cmd) - except Exception as exc: + except CalledProcessError as exc: print("Failed command", cmd, "with:", exc) if exists("install.log"): with open("install.log") as fh: diff --git a/ci/appveyor-download.py b/ci/appveyor-download.py old mode 100644 new mode 100755 index fa5e80d..17cc9e5 --- a/ci/appveyor-download.py +++ b/ci/appveyor-download.py @@ -1,3 +1,4 @@ +#!/usr/bin/env python """ Use the AppVeyor API to download Windows artifacts. @@ -5,6 +6,8 @@ # Licensed under the Apache License: http://www.apache.org/licenses/LICENSE-2.0 # For details: https://bitbucket.org/ned/coveragepy/src/default/NOTICE.txt """ +from __future__ import unicode_literals + import argparse import os import zipfile @@ -14,12 +17,13 @@ def make_auth_headers(): """Make the authentication headers needed to use the Appveyor API.""" - if not os.path.exists(".appveyor.token"): + path = os.path.expanduser("~/.appveyor.token") + if not os.path.exists(path): raise RuntimeError( - "Please create a file named `.appveyor.token` in the current directory. " + "Please create a file named `.appveyor.token` in your home directory. " "You can get the token from https://ci.appveyor.com/api-token" ) - with open(".appveyor.token") as f: + with open(path) as f: token = f.read().strip() headers = { @@ -28,41 +32,30 @@ def make_auth_headers(): return headers -def make_url(url, **kwargs): - """Build an Appveyor API url.""" - return "https://ci.appveyor.com/api" + url.format(**kwargs) - - -def get_project_build(account_project): - """Get the details of the latest Appveyor build.""" - url = make_url("/projects/{account_project}", account_project=account_project) - response = requests.get(url, headers=make_auth_headers()) - return response.json() - - -def download_latest_artifacts(account_project): +def download_latest_artifacts(account_project, build_id): """Download all the artifacts from the latest build.""" - build = get_project_build(account_project) + if build_id is None: + url = "https://ci.appveyor.com/api/projects/{}".format(account_project) + else: + url = "https://ci.appveyor.com/api/projects/{}/build/{}".format(account_project, build_id) + build = requests.get(url, headers=make_auth_headers()).json() jobs = build['build']['jobs'] - print("Build {0[build][version]}, {1} jobs: {0[build][message]}".format(build, len(jobs))) + print(u"Build {0[build][version]}, {1} jobs: {0[build][message]}".format(build, len(jobs))) + for job in jobs: - name = job['name'].partition(':')[2].split(',')[0].strip() - print(" {0}: {1[status]}, {1[artifactsCount]} artifacts".format(name, job)) + name = job['name'] + print(u" {0}: {1[status]}, {1[artifactsCount]} artifacts".format(name, job)) - url = make_url("/buildjobs/{jobid}/artifacts", jobid=job['jobId']) + url = "https://ci.appveyor.com/api/buildjobs/{}/artifacts".format(job['jobId']) response = requests.get(url, headers=make_auth_headers()) artifacts = response.json() for artifact in artifacts: is_zip = artifact['type'] == "Zip" filename = artifact['fileName'] - print(" {0}, {1} bytes".format(filename, artifact['size'])) + print(u" {0}, {1} bytes".format(filename, artifact['size'])) - url = make_url( - "/buildjobs/{jobid}/artifacts/{filename}", - jobid=job['jobId'], - filename=filename - ) + url = "https://ci.appveyor.com/api/buildjobs/{}/artifacts/{}".format(job['jobId'], filename) download_url(url, filename, make_auth_headers()) if is_zip: @@ -72,7 +65,7 @@ def download_latest_artifacts(account_project): def ensure_dirs(filename): """Make sure the directories exist for `filename`.""" - dirname, _ = os.path.split(filename) + dirname = os.path.dirname(filename) if dirname and not os.path.exists(dirname): os.makedirs(dirname) @@ -85,6 +78,8 @@ def download_url(url, filename, headers): with open(filename, 'wb') as f: for chunk in response.iter_content(16 * 1024): f.write(chunk) + else: + print(u" Error downloading {}: {}".format(url, response)) def unpack_zipfile(filename): @@ -92,17 +87,23 @@ def unpack_zipfile(filename): with open(filename, 'rb') as fzip: z = zipfile.ZipFile(fzip) for name in z.namelist(): - print(" extracting {}".format(name)) + print(u" extracting {}".format(name)) ensure_dirs(name) z.extract(name) + parser = argparse.ArgumentParser(description='Download artifacts from AppVeyor.') -parser.add_argument('name', - metavar='ID', - help='Project ID in AppVeyor. Example: ionelmc/python-nameless') +parser.add_argument('--id', + metavar='PROJECT_ID', + default='ionelmc/python-fields', + help='Project ID in AppVeyor.') +parser.add_argument('build', + nargs='?', + metavar='BUILD_ID', + help='Build ID in AppVeyor. Eg: master-123') if __name__ == "__main__": # import logging # logging.basicConfig(level="DEBUG") args = parser.parse_args() - download_latest_artifacts(args.name) + download_latest_artifacts(args.id, args.build) diff --git a/docs/conf.py b/docs/conf.py index 3e464c2..33f3bdb 100644 --- a/docs/conf.py +++ b/docs/conf.py @@ -7,11 +7,13 @@ extensions = [ 'sphinx.ext.autodoc', 'sphinx.ext.autosummary', - 'sphinx.ext.todo', 'sphinx.ext.coverage', + 'sphinx.ext.doctest', + 'sphinx.ext.extlinks', 'sphinx.ext.ifconfig', + 'sphinx.ext.napoleon', + 'sphinx.ext.todo', 'sphinx.ext.viewcode', - 'sphinxcontrib.napoleon' ] if os.getenv('SPELLCHECK'): extensions += 'sphinxcontrib.spelling', @@ -21,10 +23,17 @@ source_suffix = '.rst' master_doc = 'index' project = 'Fields' -year = '2014-2015' +year = '2014-2016' author = 'Ionel Cristian Mărieș' copyright = '{0}, {1}'.format(year, author) version = release = '5.0.0' + +pygments_style = 'trac' +templates_path = ['.'] +extlinks = { + 'issue': ('https://github.com/ionelmc/python-fields/issues/%s', '#'), + 'pr': ('https://github.com/ionelmc/python-fields/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()] @@ -32,12 +41,14 @@ 'githuburl': 'https://github.com/ionelmc/python-fields/' } -pygments_style = 'trac' -templates_path = ['.'] 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'], } html_short_title = '%s-%s' % (project, version) + +napoleon_use_ivar = True +napoleon_use_rtype = False +napoleon_use_param = False diff --git a/docs/index.rst b/docs/index.rst index 61006d9..40f35b5 100644 --- a/docs/index.rst +++ b/docs/index.rst @@ -1,7 +1,6 @@ -Welcome to Fields's documentation! -==================================== - -Contents: +======== +Contents +======== .. toctree:: :maxdepth: 2 diff --git a/docs/readme.rst b/docs/readme.rst index 6be290b..72a3355 100644 --- a/docs/readme.rst +++ b/docs/readme.rst @@ -1,5 +1 @@ -######## -Overview -######## - .. include:: ../README.rst diff --git a/docs/requirements.txt b/docs/requirements.txt index 1632a96..ef4a013 100644 --- a/docs/requirements.txt +++ b/docs/requirements.txt @@ -1,4 +1,3 @@ -sphinx -sphinxcontrib-napoleon +sphinx>=1.3 sphinx-py3doc-enhanced-theme -e . diff --git a/setup.cfg b/setup.cfg index ed67df8..4f857e3 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/* diff --git a/setup.py b/setup.py index 6a09b6a..ae6f4fb 100644 --- a/setup.py +++ b/setup.py @@ -1,15 +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 @@ -23,13 +22,15 @@ def read(*names, **kwargs): ).read() - setup( name='fields', version='5.0.0', license='BSD', description='Container class boilerplate killer.', - long_description='%s\n%s' % (read('README.rst'), re.sub(':[a-z]+:`~?(.*?)`', r'``\1``', read('CHANGELOG.rst'))), + 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-fields', diff --git a/tox.ini b/tox.ini index d31afda..4e0e303 100644 --- a/tox.ini +++ b/tox.ini @@ -71,9 +71,8 @@ commands = deps = coveralls skip_install = true -usedevelop = false commands = - coverage combine + coverage combine --append coverage report coveralls [] @@ -81,27 +80,24 @@ commands = deps = codecov skip_install = true -usedevelop = false commands = - coverage combine + coverage combine --append coverage report coverage xml --ignore-errors codecov [] [testenv:report] -basepython = python3.4 deps = coverage skip_install = true -usedevelop = false commands = - coverage combine + coverage combine --append coverage report + coverage html [testenv:clean] commands = coverage erase skip_install = true -usedevelop = false deps = coverage [testenv:2.6-nocov] From 31497cf61e277b79429e5e3d7942744f56574b25 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ionel=20Cristian=20M=C4=83rie=C8=99?= Date: Mon, 22 Aug 2016 02:05:12 +0300 Subject: [PATCH 04/16] Fix manifest. --- MANIFEST.in | 1 + 1 file changed, 1 insertion(+) diff --git a/MANIFEST.in b/MANIFEST.in index 92351c4..842b2e4 100644 --- a/MANIFEST.in +++ b/MANIFEST.in @@ -17,5 +17,6 @@ include LICENSE include README.rst include tox.ini .travis.yml appveyor.yml +include conftest.py global-exclude *.py[cod] __pycache__ *.so *.dylib From f2958e57891696bb1d46713a7232b2d7279125f1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ionel=20Cristian=20M=C4=83rie=C8=99?= Date: Sat, 4 Feb 2017 17:35:21 +0200 Subject: [PATCH 05/16] Test cnamedtuple too. --- tests/test_perf.py | 6 +++++- tox.ini | 5 +++-- 2 files changed, 8 insertions(+), 3 deletions(-) diff --git a/tests/test_perf.py b/tests/test_perf.py index 52ae30b..47272a1 100644 --- a/tests/test_perf.py +++ b/tests/test_perf.py @@ -7,6 +7,7 @@ from attr import make_class from characteristic import Attribute from characteristic import attributes +from cnamedtuple import namedtuple as cnamedtuple from fields import Fields from fields import SlotsFields @@ -68,7 +69,7 @@ def __init__(self, a, b, c="abc"): self.c = c namedtuple_class = namedtuple("namedtuple_class", ["a", "b", "c"]) - +cnamedtuple_class = cnamedtuple("namedtuple_class", ["a", "b", "c"]) attrs_class = make_class("attrs_class", ["a", "b", "c"]) @@ -103,6 +104,9 @@ def test_tuple(benchmark): def test_namedtuple(benchmark): assert benchmark(partial(namedtuple_class, a=1, b=2, c=1)) +def test_cnamedtuple(benchmark): + assert benchmark(partial(cnamedtuple_class, a=1, b=2, c=1)) + def test_attrs_decorated_class(benchmark): assert benchmark(partial(attrs_decorated_class, a=1, b=2, c=1)) diff --git a/tox.ini b/tox.ini index 4e0e303..e4791ec 100644 --- a/tox.ini +++ b/tox.ini @@ -24,9 +24,10 @@ passenv = * deps = pytest - pytest-benchmark + pytest-benchmark==3.1a1 characteristic==14.3.0 - attrs==15.2.0 + attrs==16.3.0 + cnamedtuple==0.1.6 cover: pytest-cov commands = nocov: {posargs:py.test -vv --ignore=src} From f59c35c0d4cc32529e2c24306e81f3586e7348c8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ionel=20Cristian=20M=C4=83rie=C8=99?= Date: Thu, 23 Mar 2017 16:36:45 +0200 Subject: [PATCH 06/16] Update skel, add tests for 3.6. --- .bumpversion.cfg | 2 + .cookiecutterrc | 2 +- .travis.yml | 64 +++++++++++++--------- CONTRIBUTING.rst | 2 +- LICENSE | 2 +- README.rst | 19 +++---- appveyor.yml | 108 +++++++++++++++++++++++++++++--------- ci/appveyor-bootstrap.py | 23 ++++---- ci/bootstrap.py | 57 ++++++++++++++++++++ ci/templates/.travis.yml | 36 +++++++++++++ ci/templates/appveyor.yml | 53 +++++++++++++++++++ setup.cfg | 2 +- setup.py | 1 + tox.ini | 64 +++++++++++----------- 14 files changed, 330 insertions(+), 105 deletions(-) create mode 100755 ci/bootstrap.py create mode 100644 ci/templates/.travis.yml create mode 100644 ci/templates/appveyor.yml diff --git a/.bumpversion.cfg b/.bumpversion.cfg index b3a8c64..55cc38e 100644 --- a/.bumpversion.cfg +++ b/.bumpversion.cfg @@ -5,6 +5,8 @@ tag = True [bumpversion:file:setup.py] +[bumpversion:file:README.rst] + [bumpversion:file:docs/conf.py] [bumpversion:file:src/fields/__init__.py] diff --git a/.cookiecutterrc b/.cookiecutterrc index f450174..d85296d 100644 --- a/.cookiecutterrc +++ b/.cookiecutterrc @@ -31,4 +31,4 @@ cookiecutter: travis: 'yes' version: 5.0.0 website: https://blog.ionelmc.ro - year: 2014-2016 + year: 2014-2017 diff --git a/.travis.yml b/.travis.yml index ce023a6..5534585 100644 --- a/.travis.yml +++ b/.travis.yml @@ -1,31 +1,52 @@ language: python -python: '3.5' sudo: false +cache: pip env: global: - LD_PRELOAD=/lib/x86_64-linux-gnu/libSegFault.so - SEGFAULT_SIGNALS=all matrix: - TOXENV=check + - TOXENV=docs +matrix: + include: - - TOXENV=2.6-cover,coveralls,codecov - - TOXENV=2.6-nocov - - - TOXENV=2.7-cover,coveralls,codecov - - TOXENV=2.7-nocov - - - TOXENV=3.3-cover,coveralls,codecov - - TOXENV=3.3-nocov - - - TOXENV=3.4-cover,coveralls,codecov - - TOXENV=3.4-nocov - - - TOXENV=3.5-cover,coveralls,codecov - - TOXENV=3.5-nocov - - - TOXENV=pypy-cover,coveralls,codecov - - TOXENV=pypy-nocov - + - python: '2.7' + env: + - TOXENV=py27-cover,report,coveralls,codecov + - python: '2.7' + env: + - TOXENV=py27-nocov + - python: '3.3' + env: + - TOXENV=py33-cover,report,coveralls,codecov + - python: '3.3' + env: + - TOXENV=py33-nocov + - python: '3.4' + env: + - TOXENV=py34-cover,report,coveralls,codecov + - python: '3.4' + env: + - TOXENV=py34-nocov + - python: '3.5' + env: + - TOXENV=py35-cover,report,coveralls,codecov + - python: '3.5' + env: + - TOXENV=py35-nocov + - python: '3.6' + env: + - TOXENV=py36-cover,report,coveralls,codecov + - python: '3.6' + env: + - TOXENV=py36-nocov + - python: 'pypy' + env: + - TOXENV=pypy-cover,report,coveralls,codecov + - python: 'pypy' + env: + - TOXENV=pypy-nocov before_install: - python --version - uname -a @@ -41,11 +62,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 36e7155..08a8639 100644 --- a/CONTRIBUTING.rst +++ b/CONTRIBUTING.rst @@ -49,7 +49,7 @@ To set up `python-fields` 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, doc builder and spell checker with `tox `_ one command:: tox diff --git a/LICENSE b/LICENSE index 9ac3fbe..70191e6 100644 --- a/LICENSE +++ b/LICENSE @@ -1,4 +1,4 @@ -Copyright (c) 2014-2016, Ionel Cristian Mărieș +Copyright (c) 2014-2017, 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/README.rst b/README.rst index 614975f..70036fe 100644 --- a/README.rst +++ b/README.rst @@ -14,7 +14,8 @@ Overview | |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-fields/badge/?style=flat :target: https://readthedocs.org/projects/python-fields @@ -52,27 +53,27 @@ Overview :target: https://codeclimate.com/github/ionelmc/python-fields :alt: CodeClimate Quality Status -.. |version| image:: https://img.shields.io/pypi/v/fields.svg?style=flat +.. |version| image:: https://img.shields.io/pypi/v/fields.svg :alt: PyPI Package latest release :target: https://pypi.python.org/pypi/fields -.. |downloads| image:: https://img.shields.io/pypi/dm/fields.svg?style=flat - :alt: PyPI Package monthly downloads - :target: https://pypi.python.org/pypi/fields +.. |commits-since| image:: https://img.shields.io/github/commits-since/ionelmc/python-fields/v5.0.0.svg + :alt: Commits since latest release + :target: https://github.com/ionelmc/python-fields/compare/v5.0.0...master -.. |wheel| image:: https://img.shields.io/pypi/wheel/fields.svg?style=flat +.. |wheel| image:: https://img.shields.io/pypi/wheel/fields.svg :alt: PyPI Wheel :target: https://pypi.python.org/pypi/fields -.. |supported-versions| image:: https://img.shields.io/pypi/pyversions/fields.svg?style=flat +.. |supported-versions| image:: https://img.shields.io/pypi/pyversions/fields.svg :alt: Supported versions :target: https://pypi.python.org/pypi/fields -.. |supported-implementations| image:: https://img.shields.io/pypi/implementation/fields.svg?style=flat +.. |supported-implementations| image:: https://img.shields.io/pypi/implementation/fields.svg :alt: Supported implementations :target: https://pypi.python.org/pypi/fields -.. |scrutinizer| image:: https://img.shields.io/scrutinizer/g/ionelmc/python-fields/master.svg?style=flat +.. |scrutinizer| image:: https://img.shields.io/scrutinizer/g/ionelmc/python-fields/master.svg :alt: Scrutinizer Status :target: https://scrutinizer-ci.com/g/ionelmc/python-fields/ diff --git a/appveyor.yml b/appveyor.yml index 889906b..bf70a6a 100644 --- a/appveyor.yml +++ b/appveyor.yml @@ -11,76 +11,130 @@ environment: PYTHON_VERSION: '2.7' PYTHON_ARCH: '32' - - TOXENV: '2.7-cover,codecov' - TOXPYTHON: C:\Python27\python.exe - PYTHON_HOME: C:\Python27 - PYTHON_VERSION: '2.7' - PYTHON_ARCH: '32' - - TOXENV: '2.7-nocov' + - TOXENV: 'py27-cover,codecov' TOXPYTHON: C:\Python27\python.exe PYTHON_HOME: C:\Python27 PYTHON_VERSION: '2.7' PYTHON_ARCH: '32' - - TOXENV: '2.7-cover,codecov' + - TOXENV: 'py27-cover,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-nocov' + + - TOXENV: 'py27-nocov' + TOXPYTHON: C:\Python27\python.exe + PYTHON_HOME: C:\Python27 + PYTHON_VERSION: '2.7' + PYTHON_ARCH: '32' + + - TOXENV: 'py27-nocov' 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.4-cover,codecov' - TOXPYTHON: C:\Python34\python.exe - PYTHON_HOME: C:\Python34 - PYTHON_VERSION: '3.4' + - TOXENV: 'py33-cover,codecov' + TOXPYTHON: C:\Python33\python.exe + PYTHON_HOME: C:\Python33 + PYTHON_VERSION: '3.3' + PYTHON_ARCH: '32' + + - TOXENV: 'py33-cover,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: 'py33-nocov' + TOXPYTHON: C:\Python33\python.exe + PYTHON_HOME: C:\Python33 + PYTHON_VERSION: '3.3' PYTHON_ARCH: '32' - - TOXENV: '3.4-nocov' + + - TOXENV: 'py33-nocov' + TOXPYTHON: C:\Python33-x64\python.exe + WINDOWS_SDK_VERSION: v7.1 + PYTHON_HOME: C:\Python33-x64 + PYTHON_VERSION: '3.3' + PYTHON_ARCH: '64' + + - TOXENV: 'py34-cover,codecov' TOXPYTHON: C:\Python34\python.exe PYTHON_HOME: C:\Python34 PYTHON_VERSION: '3.4' PYTHON_ARCH: '32' - - TOXENV: '3.4-cover,codecov' + - TOXENV: 'py34-cover,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-nocov' + + - TOXENV: 'py34-nocov' + TOXPYTHON: C:\Python34\python.exe + PYTHON_HOME: C:\Python34 + PYTHON_VERSION: '3.4' + PYTHON_ARCH: '32' + + - TOXENV: 'py34-nocov' 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-cover,codecov' + - TOXENV: 'py35-cover,codecov' TOXPYTHON: C:\Python35\python.exe PYTHON_HOME: C:\Python35 PYTHON_VERSION: '3.5' PYTHON_ARCH: '32' - - TOXENV: '3.5-nocov' + + - TOXENV: 'py35-cover,codecov' + TOXPYTHON: C:\Python35-x64\python.exe + PYTHON_HOME: C:\Python35-x64 + PYTHON_VERSION: '3.5' + PYTHON_ARCH: '64' + + - TOXENV: 'py35-nocov' TOXPYTHON: C:\Python35\python.exe PYTHON_HOME: C:\Python35 PYTHON_VERSION: '3.5' PYTHON_ARCH: '32' - - TOXENV: '3.5-cover,codecov' + - TOXENV: 'py35-nocov' TOXPYTHON: C:\Python35-x64\python.exe - WINDOWS_SDK_VERSION: v7.1 PYTHON_HOME: C:\Python35-x64 PYTHON_VERSION: '3.5' PYTHON_ARCH: '64' - - TOXENV: '3.5-nocov' - TOXPYTHON: C:\Python35-x64\python.exe - WINDOWS_SDK_VERSION: v7.1 - PYTHON_HOME: C:\Python35-x64 - PYTHON_VERSION: '3.5' + + - TOXENV: 'py36-cover,codecov' + TOXPYTHON: C:\Python36\python.exe + PYTHON_HOME: C:\Python36 + PYTHON_VERSION: '3.6' + PYTHON_ARCH: '32' + + - TOXENV: 'py36-cover,codecov' + TOXPYTHON: C:\Python36-x64\python.exe + PYTHON_HOME: C:\Python36-x64 + PYTHON_VERSION: '3.6' + PYTHON_ARCH: '64' + + - TOXENV: 'py36-nocov' + TOXPYTHON: C:\Python36\python.exe + PYTHON_HOME: C:\Python36 + PYTHON_VERSION: '3.6' + PYTHON_ARCH: '32' + + - TOXENV: 'py36-nocov' + TOXPYTHON: C:\Python36-x64\python.exe + PYTHON_HOME: C:\Python36-x64 + PYTHON_VERSION: '3.6' PYTHON_ARCH: '64' init: @@ -100,5 +154,7 @@ on_failure: - ps: get-content .tox\*\log\* artifacts: - path: dist\* -### To enable remote debugging uncomment this: -# - ps: $blockRdp = $true; iex ((new-object net.webclient).DownloadString('https://raw.githubusercontent.com/appveyor/ci/master/scripts/enable-rdp.ps1')) + +### 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 index b411889..1a9d198 100644 --- a/ci/appveyor-bootstrap.py +++ b/ci/appveyor-bootstrap.py @@ -5,10 +5,8 @@ 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 CalledProcessError from subprocess import check_call try: @@ -22,15 +20,17 @@ URLS = { ("2.6", "64"): BASE_URL + "2.6.6/python-2.6.6.amd64.msi", ("2.6", "32"): BASE_URL + "2.6.6/python-2.6.6.msi", - ("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", + ("2.7", "64"): BASE_URL + "2.7.10/python-2.7.13.amd64.msi", + ("2.7", "32"): BASE_URL + "2.7.10/python-2.7.13.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", + ("3.3", "64"): BASE_URL + "3.3.3/python-3.3.5.amd64.msi", + ("3.3", "32"): BASE_URL + "3.3.3/python-3.3.5.msi", + ("3.4", "64"): BASE_URL + "3.4.3/python-3.4.6.amd64.msi", + ("3.4", "32"): BASE_URL + "3.4.3/python-3.4.6.msi", + ("3.5", "64"): BASE_URL + "3.5.0/python-3.5.3-amd64.exe", + ("3.5", "32"): BASE_URL + "3.5.0/python-3.5.3.exe", + ("3.6", "64"): BASE_URL + "3.6.0/python-3.6.0-amd64.exe", + ("3.6", "32"): BASE_URL + "3.6.0/python-3.6.0.exe", } INSTALL_CMD = { # Commands are allowed to fail only if they are not the last command. Eg: uninstall (/x) allowed to fail. @@ -43,6 +43,7 @@ "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}"]], + "3.6": [["{path}", "/quiet", "TargetDir={home}"]], } @@ -73,7 +74,7 @@ def install_python(version, arch, home): print("Running:", " ".join(cmd)) try: check_call(cmd) - except CalledProcessError as exc: + except Exception as exc: print("Failed command", cmd, "with:", exc) if exists("install.log"): with open("install.log") as fh: diff --git a/ci/bootstrap.py b/ci/bootstrap.py new file mode 100755 index 0000000..e5292aa --- /dev/null +++ b/ci/bootstrap.py @@ -0,0 +1,57 @@ +#!/usr/bin/env python +# -*- coding: utf-8 -*- +from __future__ import absolute_import, print_function, unicode_literals + +import os +import sys +from os.path import abspath +from os.path import dirname +from os.path import exists +from os.path import join + + +if __name__ == "__main__": + 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(env_path, "Scripts") + else: + 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 subprocess.CalledProcessError: + subprocess.check_call([sys.executable, "-m", "virtualenv", env_path]) + print("Installing `jinja2` into bootstrap environment...") + subprocess.check_call([join(bin_path, "pip"), "install", "jinja2"]) + activate = join(bin_path, "activate_this.py") + # noinspection PyCompatibility + exec(compile(open(activate, "rb").read(), activate, "exec"), dict(__file__=activate)) + + import jinja2 + + import subprocess + + jinja = jinja2.Environment( + loader=jinja2.FileSystemLoader(join(base_path, "ci", "templates")), + trim_blocks=True, + lstrip_blocks=True, + keep_trailing_newline=True + ) + + tox_environments = [ + line.strip() + # WARNING: 'tox' must be installed globally or in the project's virtualenv + for line in subprocess.check_output(['tox', '--listenvs'], universal_newlines=True).splitlines() + ] + tox_environments = [line for line in tox_environments if line not in ['clean', 'report', 'docs', 'check']] + + 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.") diff --git a/ci/templates/.travis.yml b/ci/templates/.travis.yml new file mode 100644 index 0000000..9704d89 --- /dev/null +++ b/ci/templates/.travis.yml @@ -0,0 +1,36 @@ +language: python +sudo: false +cache: pip +env: + global: + - LD_PRELOAD=/lib/x86_64-linux-gnu/libSegFault.so + - SEGFAULT_SIGNALS=all + matrix: + - TOXENV=check + - TOXENV=docs +matrix: + include: +{% for env in tox_environments %}{{ '' }} + - python: '{{ env.split('-')[0] if env.startswith('pypy') else '{0[2]}.{0[3]}'.format(env) }}' + env: + - TOXENV={{ env }}{% if 'cover' in env %},report,coveralls,codecov{% endif -%} +{% endfor %}{{ '' }} +before_install: + - python --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 new file mode 100644 index 0000000..be9728d --- /dev/null +++ b/ci/templates/appveyor.yml @@ -0,0 +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' + matrix: + - TOXENV: check + PYTHON_HOME: C:\Python27 + PYTHON_VERSION: '2.7' + PYTHON_ARCH: '32' + +{% for env in tox_environments %}{% if env.startswith(('py2', 'py3')) %} + - TOXENV: '{{ env }}{% if 'cover' in env %},codecov{% endif %}' + TOXPYTHON: C:\Python{{ env[2:4] }}\python.exe + PYTHON_HOME: C:\Python{{ env[2:4] }} + PYTHON_VERSION: '{{ env[2] }}.{{ env[3] }}' + PYTHON_ARCH: '32' + + - TOXENV: '{{ env }}{% if 'cover' in env %},codecov{%- endif %}' + TOXPYTHON: C:\Python{{ env[2:4] }}-x64\python.exe + {%- if env.startswith(('py2', 'py33', 'py34')) %} + + WINDOWS_SDK_VERSION: v7.{{ '1' if env.startswith('py3') else '0' }} + {%- endif %} + + PYTHON_HOME: C:\Python{{ env[2:4] }}-x64 + PYTHON_VERSION: '{{ env[2] }}.{{ env[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/setup.cfg b/setup.cfg index 4f857e3..4c0ea4a 100644 --- a/setup.cfg +++ b/setup.cfg @@ -5,7 +5,7 @@ universal = 1 max-line-length = 140 exclude = tests/*,*/migrations/*,*/south_migrations/* -[pytest] +[tool:pytest] norecursedirs = .git .tox diff --git a/setup.py b/setup.py index ae6f4fb..60e1891 100644 --- a/setup.py +++ b/setup.py @@ -54,6 +54,7 @@ def read(*names, **kwargs): 'Programming Language :: Python :: 3.3', 'Programming Language :: Python :: 3.4', 'Programming Language :: Python :: 3.5', + 'Programming Language :: Python :: 3.6', 'Programming Language :: Python :: Implementation :: CPython', 'Programming Language :: Python :: Implementation :: PyPy', 'Topic :: Utilities', diff --git a/tox.ini b/tox.ini index e4791ec..a3cd882 100644 --- a/tox.ini +++ b/tox.ini @@ -4,26 +4,28 @@ envlist = clean, check, - {2.6,2.7,3.3,3.4,3.5,pypy}-{cover,nocov}, + {py27,py33,py34,py35,py36,pypy}-{cover,nocov}, report, docs [testenv] basepython = pypy: {env:TOXPYTHON:pypy} - 2.6: {env:TOXPYTHON:python2.6} - {2.7,docs,spell}: {env:TOXPYTHON:python2.7} - 3.3: {env:TOXPYTHON:python3.3} - 3.4: {env:TOXPYTHON:python3.4} - 3.5: {env:TOXPYTHON:python3.5} - {clean,check,report,extension-coveralls,coveralls,codecov}: python3.4 + {py27,docs,spell}: {env:TOXPYTHON:python2.7} + py33: {env:TOXPYTHON:python3.3} + py34: {env:TOXPYTHON:python3.4} + py35: {env:TOXPYTHON:python3.5} + py36: {env:TOXPYTHON:python3.6} + {bootstrap,clean,check,report,coveralls,codecov}: python3 setenv = PYTHONPATH={toxinidir}/tests PYTHONUNBUFFERED=yes passenv = * +usedevelop = false deps = pytest + pytest-travis-fold pytest-benchmark==3.1a1 characteristic==14.3.0 attrs==16.3.0 @@ -32,7 +34,16 @@ deps = commands = nocov: {posargs:py.test -vv --ignore=src} cover: {posargs:py.test --cov --cov-report=term-missing -vv} -usedevelop = true + +[testenv:bootstrap] +deps = + jinja2 + matrix +skip_install = true +commands = + python ci/bootstrap.py +passenv = + * [testenv:spell] setenv = @@ -40,7 +51,6 @@ setenv = commands = sphinx-build -b spelling docs dist/docs skip_install = true -usedevelop = true deps = -r{toxinidir}/docs/requirements.txt sphinxcontrib-spelling @@ -54,27 +64,25 @@ commands = sphinx-build -b linkcheck docs dist/docs [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 = coveralls skip_install = true commands = - coverage combine --append - coverage report coveralls [] [testenv:codecov] @@ -82,8 +90,6 @@ deps = codecov skip_install = true commands = - coverage combine --append - coverage report coverage xml --ignore-errors codecov [] @@ -101,21 +107,17 @@ commands = coverage erase skip_install = true deps = coverage -[testenv:2.6-nocov] -usedevelop = false - -[testenv:2.7-nocov] -usedevelop = false - -[testenv:3.3-nocov] -usedevelop = false +[testenv:py27-cover] +usedevelop = true -[testenv:3.4-nocov] -usedevelop = false +[testenv:py33-cover] +usedevelop = true -[testenv:3.5-nocov] -usedevelop = false +[testenv:py34-cover] +usedevelop = true -[testenv:pypy-nocov] -usedevelop = false +[testenv:py35-cover] +usedevelop = true +[testenv:pypy-cover] +usedevelop = true From 9c2eaa1a1b56bd95ecf064d6b0f66caeba3d256b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ionel=20Cristian=20M=C4=83rie=C8=99?= Date: Thu, 23 Mar 2017 16:41:44 +0200 Subject: [PATCH 07/16] Skip cnamedtuple in some places. --- tests/test_perf.py | 18 ++++++++++++++++-- tox.ini | 2 +- 2 files changed, 17 insertions(+), 3 deletions(-) diff --git a/tests/test_perf.py b/tests/test_perf.py index 47272a1..8073185 100644 --- a/tests/test_perf.py +++ b/tests/test_perf.py @@ -1,13 +1,13 @@ from collections import namedtuple from functools import partial +import pytest from attr import ib as badly_named_field from attr import s as badly_named_class_decorator from attr import Factory from attr import make_class from characteristic import Attribute from characteristic import attributes -from cnamedtuple import namedtuple as cnamedtuple from fields import Fields from fields import SlotsFields @@ -17,6 +17,11 @@ from fields import factory from fields import make_init_func +try: + from cnamedtuple import namedtuple as cnamedtuple +except ImportError: + cnamedtuple = None + @attributes(["a", "b", Attribute("c", default_value="abc")]) class characteristic_class(object): @@ -59,17 +64,25 @@ def __init__(self, a, b, c="abc"): self.b = b self.c = c super(super_dumb_class, self).__init__(a=a, b=b, c=c) + return super_dumb_class + + super_dumb_class = make_super_dumb_class() + class dumb_class(object): def __init__(self, a, b, c="abc"): self.a = a self.b = b self.c = c + namedtuple_class = namedtuple("namedtuple_class", ["a", "b", "c"]) -cnamedtuple_class = cnamedtuple("namedtuple_class", ["a", "b", "c"]) +if cnamedtuple: + cnamedtuple_class = cnamedtuple("namedtuple_class", ["a", "b", "c"]) +else: + cnamedtuple_class = lambda *args, **kwargs: pytest.skip("Not available.") attrs_class = make_class("attrs_class", ["a", "b", "c"]) @@ -104,6 +117,7 @@ def test_tuple(benchmark): def test_namedtuple(benchmark): assert benchmark(partial(namedtuple_class, a=1, b=2, c=1)) + def test_cnamedtuple(benchmark): assert benchmark(partial(cnamedtuple_class, a=1, b=2, c=1)) diff --git a/tox.ini b/tox.ini index a3cd882..eb07c1a 100644 --- a/tox.ini +++ b/tox.ini @@ -29,7 +29,7 @@ deps = pytest-benchmark==3.1a1 characteristic==14.3.0 attrs==16.3.0 - cnamedtuple==0.1.6 + {py34,py35,py36}: cnamedtuple==0.1.6 cover: pytest-cov commands = nocov: {posargs:py.test -vv --ignore=src} From 7ef0835729603edb88718a0fa4df5395210c5cea Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ionel=20Cristian=20M=C4=83rie=C8=99?= Date: Thu, 23 Mar 2017 17:01:41 +0200 Subject: [PATCH 08/16] Styling. --- src/fields/__init__.py | 1 + src/fields/extras.py | 3 ++- tests/test_fields.py | 14 ++++++++------ 3 files changed, 11 insertions(+), 7 deletions(-) diff --git a/src/fields/__init__.py b/src/fields/__init__.py index d5fd009..5cff120 100644 --- a/src/fields/__init__.py +++ b/src/fields/__init__.py @@ -435,6 +435,7 @@ def factory(sealer, **sealer_options): """ return _Factory(sealer=_SealerWrapper(sealer, **sealer_options)) + Fields = _Factory() ConvertibleFields = factory(class_sealer, convertible=True) SlotsFields = factory(slots_class_sealer) diff --git a/src/fields/extras.py b/src/fields/extras.py index ce247a2..cf699f9 100644 --- a/src/fields/extras.py +++ b/src/fields/extras.py @@ -1,8 +1,8 @@ import re from fields import __base__ -from fields import _SealerWrapper from fields import _Factory +from fields import _SealerWrapper class ValidationError(Exception): @@ -46,4 +46,5 @@ def __init__(self, *args, **kwargs): )) return klass + RegexValidate = _Factory(sealer=_SealerWrapper(regex_validation_sealer)) diff --git a/tests/test_fields.py b/tests/test_fields.py index 10eb595..01b4f36 100644 --- a/tests/test_fields.py +++ b/tests/test_fields.py @@ -1,27 +1,29 @@ from __future__ import print_function import pickle -try: - import cPickle -except ImportError: - import pickle as cPickle from functools import partial -from itertools import chain from pytest import fixture from pytest import raises -from fields import BareFields, make_init_func, InheritableFields +from fields import BareFields from fields import ComparableMixin from fields import ConvertibleFields from fields import ConvertibleMixin from fields import Fields +from fields import InheritableFields from fields import PrintableMixin from fields import SlotsFields from fields import Tuple +from fields import make_init_func from fields.extras import RegexValidate from fields.extras import ValidationError +try: + import cPickle +except ImportError: + import pickle as cPickle + @fixture(params=[ partial(pickle.dumps, protocol=i) From 3f946af59952834446ca403af966906c814e5a91 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ionel=20Cristian=20M=C4=83rie=C8=99?= Date: Wed, 17 May 2017 04:01:20 +0300 Subject: [PATCH 09/16] Fix format issues. --- README.rst | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/README.rst b/README.rst index 70036fe..8409d5d 100644 --- a/README.rst +++ b/README.rst @@ -320,7 +320,7 @@ Now this is a very difficult question. Consider this typical use-case:: -.. sourcecode:: pycon +.. code:: pycon >>> import attr >>> @attr.s @@ -333,12 +333,12 @@ Worth noting: * attrs_ is faster because it doesn't allow your class to be used as a mixin (it doesn't do any ``super(cls, self).__init__(...)`` for you). * The typical use-case doesn't allow you to have a custom ``__init__``. You can - use `@attr.s(init=False)` that will allow you to implement your own ``__init__``. + use ``@attr.s(init=False)`` that will allow you to implement your own ``__init__``. However, you can't have your own ``__init__`` that calls attrs_ provided ``__init__`` (like in a subclassing scenario). * It works better with IDEs and source code analysis tools because of the attributes defined on the class. -* It's more composable if you only use `@attr.s` decorated hierarchies. Example: +* It's more composable if you only use ``@attr.s`` decorated hierarchies. Example: .. sourcecode:: pycon From b73991233b9c2d7bcd3ee4d598440aeecc95aa3a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ionel=20Cristian=20M=C4=83rie=C8=99?= Date: Wed, 17 May 2017 04:02:42 +0300 Subject: [PATCH 10/16] Fix format issue. --- README.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.rst b/README.rst index 8409d5d..a255d74 100644 --- a/README.rst +++ b/README.rst @@ -318,7 +318,7 @@ Why not ``attrs``? Now this is a very difficult question. -Consider this typical use-case:: +Consider this typical use-case: .. code:: pycon From 91e13560173abcc42cc1a95cbe2956b307126416 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ionel=20Cristian=20M=C4=83rie=C8=99?= Date: Wed, 17 May 2017 04:06:53 +0300 Subject: [PATCH 11/16] Add a note. --- README.rst | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/README.rst b/README.rst index a255d74..3a61416 100644 --- a/README.rst +++ b/README.rst @@ -357,7 +357,9 @@ Worth noting: C(a=1, b=2, c=3) All in all, attrs_ is a fast and minimal container library that does support subclasses, -but quite differently than `fields`. Definitely worth considering. +but quite differently than ``fields``. Definitely worth considering. + +Also, nowdays it has more features than ``fields``. See `#6 `_. .. _attrs: Date: Sat, 5 Oct 2019 02:42:45 +0300 Subject: [PATCH 12/16] Update skel. --- appveyor.yml => .appveyor.yml | 130 ++++++++----------- .bumpversion.cfg | 9 +- .cookiecutterrc | 57 +++++--- .gitignore | 6 + .travis.yml | 84 ++++++------ CONTRIBUTING.rst | 6 +- LICENSE | 21 +-- ci/appveyor-bootstrap.py | 120 ----------------- ci/appveyor-with-compiler.cmd | 39 ++---- ci/bootstrap.py | 61 ++++++--- ci/requirements.txt | 3 + ci/templates/{appveyor.yml => .appveyor.yml} | 39 +++--- ci/templates/.travis.yml | 32 +++-- docs/conf.py | 2 +- docs/requirements.txt | 1 - setup.cfg | 26 ++-- setup.py | 20 ++- tox.ini | 91 ++++++------- 18 files changed, 336 insertions(+), 411 deletions(-) rename appveyor.yml => .appveyor.yml (63%) delete mode 100644 ci/appveyor-bootstrap.py create mode 100644 ci/requirements.txt rename ci/templates/{appveyor.yml => .appveyor.yml} (60%) diff --git a/appveyor.yml b/.appveyor.yml similarity index 63% rename from appveyor.yml rename to .appveyor.yml index bf70a6a..d624429 100644 --- a/appveyor.yml +++ b/.appveyor.yml @@ -1,159 +1,143 @@ version: '{branch}-{build}' build: off -cache: - - '%LOCALAPPDATA%\pip\Cache' environment: global: - WITH_COMPILER: 'cmd /E:ON /V:ON /C .\ci\appveyor-with-compiler.cmd' + COVERALLS_EXTRAS: '-v' + COVERALLS_REPO_TOKEN: IoRlAEvnKbwbhBJ2jrWPqzAnE2jobA0I3 matrix: - TOXENV: check - PYTHON_HOME: C:\Python27 - PYTHON_VERSION: '2.7' + TOXPYTHON: C:\Python36\python.exe + PYTHON_HOME: C:\Python36 + PYTHON_VERSION: '3.6' PYTHON_ARCH: '32' - - - TOXENV: 'py27-cover,codecov' + - 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' + - TOXENV: py27-cover,codecov,coveralls TOXPYTHON: C:\Python27-x64\python.exe - WINDOWS_SDK_VERSION: v7.0 PYTHON_HOME: C:\Python27-x64 PYTHON_VERSION: '2.7' PYTHON_ARCH: '64' - - - TOXENV: 'py27-nocov' + WINDOWS_SDK_VERSION: v7.0 + - TOXENV: py27-nocov TOXPYTHON: C:\Python27\python.exe PYTHON_HOME: C:\Python27 PYTHON_VERSION: '2.7' PYTHON_ARCH: '32' - - - TOXENV: 'py27-nocov' + WHEEL_PATH: .tox/dist + - TOXENV: py27-nocov TOXPYTHON: C:\Python27-x64\python.exe - WINDOWS_SDK_VERSION: v7.0 PYTHON_HOME: C:\Python27-x64 PYTHON_VERSION: '2.7' PYTHON_ARCH: '64' - - - TOXENV: 'py33-cover,codecov' - TOXPYTHON: C:\Python33\python.exe - PYTHON_HOME: C:\Python33 - PYTHON_VERSION: '3.3' - PYTHON_ARCH: '32' - - - TOXENV: 'py33-cover,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: 'py33-nocov' - TOXPYTHON: C:\Python33\python.exe - PYTHON_HOME: C:\Python33 - PYTHON_VERSION: '3.3' - PYTHON_ARCH: '32' - - - TOXENV: 'py33-nocov' - TOXPYTHON: C:\Python33-x64\python.exe - WINDOWS_SDK_VERSION: v7.1 - PYTHON_HOME: C:\Python33-x64 - PYTHON_VERSION: '3.3' - PYTHON_ARCH: '64' - - - TOXENV: 'py34-cover,codecov' + WHEEL_PATH: .tox/dist + WINDOWS_SDK_VERSION: v7.0 + - TOXENV: py34-cover,codecov,coveralls TOXPYTHON: C:\Python34\python.exe PYTHON_HOME: C:\Python34 PYTHON_VERSION: '3.4' PYTHON_ARCH: '32' - - - TOXENV: 'py34-cover,codecov' + - TOXENV: py34-cover,codecov,coveralls TOXPYTHON: C:\Python34-x64\python.exe - WINDOWS_SDK_VERSION: v7.1 PYTHON_HOME: C:\Python34-x64 PYTHON_VERSION: '3.4' PYTHON_ARCH: '64' - - - TOXENV: 'py34-nocov' + WINDOWS_SDK_VERSION: v7.1 + - TOXENV: py34-nocov TOXPYTHON: C:\Python34\python.exe PYTHON_HOME: C:\Python34 PYTHON_VERSION: '3.4' PYTHON_ARCH: '32' - - - TOXENV: 'py34-nocov' + WHEEL_PATH: .tox/dist + - TOXENV: py34-nocov TOXPYTHON: C:\Python34-x64\python.exe - WINDOWS_SDK_VERSION: v7.1 PYTHON_HOME: C:\Python34-x64 PYTHON_VERSION: '3.4' PYTHON_ARCH: '64' - - - TOXENV: 'py35-cover,codecov' + WHEEL_PATH: .tox/dist + WINDOWS_SDK_VERSION: v7.1 + - 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' + - 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-nocov' + - TOXENV: py35-nocov TOXPYTHON: C:\Python35\python.exe PYTHON_HOME: C:\Python35 PYTHON_VERSION: '3.5' PYTHON_ARCH: '32' - - - TOXENV: 'py35-nocov' + WHEEL_PATH: .tox/dist + - TOXENV: py35-nocov TOXPYTHON: C:\Python35-x64\python.exe PYTHON_HOME: C:\Python35-x64 PYTHON_VERSION: '3.5' PYTHON_ARCH: '64' - - - TOXENV: 'py36-cover,codecov' + 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' + - 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-nocov' + - TOXENV: py36-nocov TOXPYTHON: C:\Python36\python.exe PYTHON_HOME: C:\Python36 PYTHON_VERSION: '3.6' PYTHON_ARCH: '32' - - - TOXENV: 'py36-nocov' + WHEEL_PATH: .tox/dist + - TOXENV: py36-nocov 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-nocov + TOXPYTHON: C:\Python37\python.exe + PYTHON_HOME: C:\Python37 + PYTHON_VERSION: '3.7' + PYTHON_ARCH: '32' + WHEEL_PATH: .tox/dist + - TOXENV: py37-nocov + TOXPYTHON: C:\Python37-x64\python.exe + PYTHON_HOME: C:\Python37-x64 + PYTHON_VERSION: '3.7' + PYTHON_ARCH: '64' + WHEEL_PATH: .tox/dist init: - ps: echo $env:TOXENV - ps: ls C:\Python* install: - - python -u ci\appveyor-bootstrap.py + - '%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: - - '%WITH_COMPILER% %PYTHON_HOME%\Scripts\tox' - + - 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\* -artifacts: - - path: dist\* ### To enable remote debugging uncomment this (also, see: http://www.appveyor.com/docs/how-to/rdp-to-build-worker): # on_finish: diff --git a/.bumpversion.cfg b/.bumpversion.cfg index 55cc38e..64b30c1 100644 --- a/.bumpversion.cfg +++ b/.bumpversion.cfg @@ -4,10 +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/fields/__init__.py] - +search = __version__ = '{current_version}' +replace = __version__ = '{new_version}' diff --git a/.cookiecutterrc b/.cookiecutterrc index d85296d..7309bb6 100644 --- a/.cookiecutterrc +++ b/.cookiecutterrc @@ -1,34 +1,55 @@ # 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' + _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: '-' + codacy: no + codacy_projectid: 862e7946eabb4112be6503a667381b71 + codeclimate: no + codecov: yes + command_line_interface: no command_line_interface_bin_name: '-' - coveralls: 'yes' + coveralls: yes + coveralls_token: IoRlAEvnKbwbhBJ2jrWPqzAnE2jobA0I3 distribution_name: fields email: contact@ionelmc.ro full_name: Ionel Cristian Mărieș - github_username: ionelmc - landscape: 'yes' + landscape: no + license: BSD 2-Clause License + linter: flake8 package_name: fields project_name: Fields project_short_description: Container class boilerplate killer. + pypi_badge: yes + pypi_disable_upload: no release_date: '2016-04-13' + repo_hosting: github.com + repo_hosting_domain: github.com repo_name: python-fields - requiresio: 'yes' - scrutinizer: 'yes' - sphinx_doctest: 'no' + 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-fields.readthedocs.io/ + sphinx_doctest: no sphinx_theme: sphinx-py3doc-enhanced-theme - test_matrix_configurator: 'no' - test_matrix_separate_coverage: 'yes' + test_matrix_configurator: no + test_matrix_separate_coverage: yes test_runner: pytest - travis: 'yes' + travis: yes + travis_osx: no version: 5.0.0 website: https://blog.ionelmc.ro - year: 2014-2017 + year_from: '2014' + year_to: '2019' diff --git a/.gitignore b/.gitignore index a74475a..486405e 100644 --- a/.gitignore +++ b/.gitignore @@ -1,4 +1,5 @@ *.py[cod] +__pycache__ # C extensions *.so @@ -21,6 +22,7 @@ lib lib64 venv*/ pyvenv*/ +pip-wheel-metadata/ # Installer logs pip-log.txt @@ -29,6 +31,7 @@ pip-log.txt .coverage .tox .coverage.* +.pytest_cache/ nosetests.xml coverage.xml htmlcov @@ -62,3 +65,6 @@ docs/_build .bootstrap .appveyor.token *.bak + +# Mypy Cache +.mypy_cache/ diff --git a/.travis.yml b/.travis.yml index 5534585..5e7ef9c 100644 --- a/.travis.yml +++ b/.travis.yml @@ -1,58 +1,68 @@ language: python -sudo: false -cache: pip +dist: xenial +cache: false env: global: - LD_PRELOAD=/lib/x86_64-linux-gnu/libSegFault.so - SEGFAULT_SIGNALS=all - matrix: - - TOXENV=check - - TOXENV=docs matrix: include: - - - python: '2.7' + - python: '3.6' env: - - TOXENV=py27-cover,report,coveralls,codecov - - python: '2.7' + - TOXENV=check + - python: '3.6' env: + - TOXENV=docs + - env: + - TOXENV=py27-cover,codecov,coveralls + python: '2.7' + - env: - TOXENV=py27-nocov - - python: '3.3' - env: - - TOXENV=py33-cover,report,coveralls,codecov - - python: '3.3' - env: - - TOXENV=py33-nocov - - python: '3.4' - env: - - TOXENV=py34-cover,report,coveralls,codecov - - python: '3.4' - env: + python: '2.7' + - env: + - TOXENV=py34-cover,codecov,coveralls + python: '3.4' + - env: - TOXENV=py34-nocov - - python: '3.5' - env: - - TOXENV=py35-cover,report,coveralls,codecov - - python: '3.5' - env: + python: '3.4' + - env: + - TOXENV=py35-cover,codecov,coveralls + python: '3.5' + - env: - TOXENV=py35-nocov - - python: '3.6' - env: - - TOXENV=py36-cover,report,coveralls,codecov - - python: '3.6' - env: + python: '3.5' + - env: + - TOXENV=py36-cover,codecov,coveralls + python: '3.6' + - env: - TOXENV=py36-nocov - - python: 'pypy' - env: - - TOXENV=pypy-cover,report,coveralls,codecov - - python: 'pypy' - env: + python: '3.6' + - env: + - TOXENV=py37-cover,codecov,coveralls + python: '3.7' + - env: + - TOXENV=py37-nocov + python: '3.7' + - env: + - TOXENV=pypy-cover,codecov,coveralls + python: 'pypy' + - env: - TOXENV=pypy-nocov + python: 'pypy' + - env: + - TOXENV=pypy3-cover,codecov,coveralls + - TOXPYTHON=pypy3 + python: 'pypy3' + - env: + - TOXENV=pypy3-nocov + - 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 diff --git a/CONTRIBUTING.rst b/CONTRIBUTING.rst index 08a8639..3b12480 100644 --- a/CONTRIBUTING.rst +++ b/CONTRIBUTING.rst @@ -41,7 +41,7 @@ To set up `python-fields` for local development: (look for the "Fork" button). 2. Clone your fork locally:: - git clone git@github.com:your_name_here/python-fields.git + git clone git@github.com:ionelmc/python-fields.git 3. Create a branch for local development:: @@ -49,7 +49,7 @@ To set up `python-fields` 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, doc builder and spell checker 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 70191e6..3871221 100644 --- a/LICENSE +++ b/LICENSE @@ -1,19 +1,10 @@ -Copyright (c) 2014-2017, Ionel Cristian Mărieș -All rights reserved. +BSD 2-Clause License -Redistribution and use in source and binary forms, with or without modification, are permitted provided that the -following conditions are met: +Copyright (c) 2014-2019, Ionel Cristian Mărieș. All rights reserved. -1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following -disclaimer. +Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: -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. + 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. +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/ci/appveyor-bootstrap.py b/ci/appveyor-bootstrap.py deleted file mode 100644 index 1a9d198..0000000 --- a/ci/appveyor-bootstrap.py +++ /dev/null @@ -1,120 +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.6", "64"): BASE_URL + "2.6.6/python-2.6.6.amd64.msi", - ("2.6", "32"): BASE_URL + "2.6.6/python-2.6.6.msi", - ("2.7", "64"): BASE_URL + "2.7.10/python-2.7.13.amd64.msi", - ("2.7", "32"): BASE_URL + "2.7.10/python-2.7.13.msi", - # NOTE: no .msi installer for 3.3.6 - ("3.3", "64"): BASE_URL + "3.3.3/python-3.3.5.amd64.msi", - ("3.3", "32"): BASE_URL + "3.3.3/python-3.3.5.msi", - ("3.4", "64"): BASE_URL + "3.4.3/python-3.4.6.amd64.msi", - ("3.4", "32"): BASE_URL + "3.4.3/python-3.4.6.msi", - ("3.5", "64"): BASE_URL + "3.5.0/python-3.5.3-amd64.exe", - ("3.5", "32"): BASE_URL + "3.5.0/python-3.5.3.exe", - ("3.6", "64"): BASE_URL + "3.6.0/python-3.6.0-amd64.exe", - ("3.6", "32"): BASE_URL + "3.6.0/python-3.6.0.exe", -} -INSTALL_CMD = { - # Commands are allowed to fail only if they are not the last command. Eg: uninstall (/x) allowed to fail. - "2.6": [["msiexec.exe", "/L*+!", "install.log", "/qn", "/x", "{path}"], - ["msiexec.exe", "/L*+!", "install.log", "/qn", "/i", "{path}", "TARGETDIR={home}"]], - "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}"]], - "3.6": [["{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 e5292aa..7bdd523 100755 --- a/ci/bootstrap.py +++ b/ci/bootstrap.py @@ -1,18 +1,28 @@ #!/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 +import jinja2 -if __name__ == "__main__": - base_path = dirname(dirname(abspath(__file__))) - print("Project path: {0}".format(base_path)) + +base_path = dirname(dirname(abspath(__file__))) + + +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") @@ -23,18 +33,24 @@ print("Making bootstrap env in: {0} ...".format(env_path)) try: - subprocess.check_call(["virtualenv", env_path]) + check_call([sys.executable, "-m", "venv", env_path]) except subprocess.CalledProcessError: - subprocess.check_call([sys.executable, "-m", "virtualenv", env_path]) + try: + check_call([sys.executable, "-m", "virtualenv", env_path]) + except subprocess.CalledProcessError: + check_call(["virtualenv", env_path]) print("Installing `jinja2` into bootstrap environment...") - subprocess.check_call([join(bin_path, "pip"), "install", "jinja2"]) - activate = join(bin_path, "activate_this.py") - # noinspection PyCompatibility - exec(compile(open(activate, "rb").read(), activate, "exec"), dict(__file__=activate)) + 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' - import jinja2 + 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 subprocess +def main(): + print("Project path: {0}".format(base_path)) jinja = jinja2.Environment( loader=jinja2.FileSystemLoader(join(base_path, "ci", "templates")), @@ -45,13 +61,28 @@ tox_environments = [ line.strip() - # WARNING: 'tox' must be installed globally or in the project's virtualenv - for line in subprocess.check_output(['tox', '--listenvs'], universal_newlines=True).splitlines() + # '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 not in ['clean', 'report', 'docs', 'check']] + tox_environments = [line for line in tox_environments if line.startswith('py')] 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..1c8d385 --- /dev/null +++ b/ci/requirements.txt @@ -0,0 +1,3 @@ +virtualenv>=16.6.0 +pip>=19.1.1 +setuptools>=18.0.1 diff --git a/ci/templates/appveyor.yml b/ci/templates/.appveyor.yml similarity index 60% rename from ci/templates/appveyor.yml rename to ci/templates/.appveyor.yml index be9728d..1c38167 100644 --- a/ci/templates/appveyor.yml +++ b/ci/templates/.appveyor.yml @@ -1,52 +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' + COVERALLS_EXTRAS: '-v' + COVERALLS_REPO_TOKEN: IoRlAEvnKbwbhBJ2jrWPqzAnE2jobA0I3 matrix: - TOXENV: check - PYTHON_HOME: C:\Python27 - PYTHON_VERSION: '2.7' + TOXPYTHON: C:\Python36\python.exe + PYTHON_HOME: C:\Python36 + PYTHON_VERSION: '3.6' PYTHON_ARCH: '32' +{% for env in tox_environments %} +{% if env.startswith(('py2', 'py3')) %} + - TOXENV: {{ env }}{% if env.endswith('-cover') %},codecov,coveralls{% endif %} -{% for env in tox_environments %}{% if env.startswith(('py2', 'py3')) %} - - TOXENV: '{{ env }}{% if 'cover' in env %},codecov{% endif %}' 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 }}{% if env.endswith('-cover') %},codecov,coveralls{% endif %} - - TOXENV: '{{ env }}{% if 'cover' in env %},codecov{%- endif %}' TOXPYTHON: C:\Python{{ env[2:4] }}-x64\python.exe - {%- if env.startswith(('py2', 'py33', 'py34')) %} - - WINDOWS_SDK_VERSION: v7.{{ '1' if env.startswith('py3') else '0' }} - {%- endif %} - 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', 'py34')) %} + WINDOWS_SDK_VERSION: v7.{{ '1' if env.startswith('py3') else '0' }} +{% endif %} {% endif %}{% endfor %} init: - ps: echo $env:TOXENV - ps: ls C:\Python* install: - - python -u ci\appveyor-bootstrap.py + - '%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: - - '%WITH_COMPILER% %PYTHON_HOME%\Scripts\tox' - + - 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\* -artifacts: - - path: dist\* ### To enable remote debugging uncomment this (also, see: http://www.appveyor.com/docs/how-to/rdp-to-build-worker): # on_finish: diff --git a/ci/templates/.travis.yml b/ci/templates/.travis.yml index 9704d89..57316b9 100644 --- a/ci/templates/.travis.yml +++ b/ci/templates/.travis.yml @@ -1,26 +1,36 @@ language: python -sudo: false -cache: pip +dist: xenial +cache: false env: global: - LD_PRELOAD=/lib/x86_64-linux-gnu/libSegFault.so - SEGFAULT_SIGNALS=all - matrix: - - TOXENV=check - - TOXENV=docs matrix: include: -{% for env in tox_environments %}{{ '' }} - - python: '{{ env.split('-')[0] if env.startswith('pypy') else '{0[2]}.{0[3]}'.format(env) }}' + - python: '3.6' env: - - TOXENV={{ env }}{% if 'cover' in env %},report,coveralls,codecov{% endif -%} -{% endfor %}{{ '' }} + - TOXENV=check + - python: '3.6' + env: + - TOXENV=docs +{%- for env in tox_environments %}{{ '' }} + - env: + - TOXENV={{ env }}{% if 'cover' in env %},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 diff --git a/docs/conf.py b/docs/conf.py index 33f3bdb..d0b3c8a 100644 --- a/docs/conf.py +++ b/docs/conf.py @@ -23,7 +23,7 @@ source_suffix = '.rst' master_doc = 'index' project = 'Fields' -year = '2014-2016' +year = '2014-2019' author = 'Ionel Cristian Mărieș' copyright = '{0}, {1}'.format(year, author) version = release = '5.0.0' diff --git a/docs/requirements.txt b/docs/requirements.txt index ef4a013..62bc14e 100644 --- a/docs/requirements.txt +++ b/docs/requirements.txt @@ -1,3 +1,2 @@ sphinx>=1.3 sphinx-py3doc-enhanced-theme --e . diff --git a/setup.cfg b/setup.cfg index 4c0ea4a..de160b4 100644 --- a/setup.cfg +++ b/setup.cfg @@ -3,23 +3,27 @@ universal = 1 [flake8] max-line-length = 140 -exclude = tests/*,*/migrations/*,*/south_migrations/* +exclude = */migrations/* [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 @@ -29,10 +33,14 @@ addopts = --doctest-glob=\*.rst --tb=short --benchmark-disable +testpaths = + tests -[isort] -force_single_line=True -line_length=120 -known_first_party=fields -default_section=THIRDPARTY -forced_separate=test_fields +[tool:isort] +force_single_line = True +line_length = 120 +known_first_party = fields +default_section = THIRDPARTY +forced_separate = test_fields +not_skip = __init__.py +skip = migrations diff --git a/setup.py b/setup.py index 60e1891..3dfb17d 100644 --- a/setup.py +++ b/setup.py @@ -16,16 +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='fields', version='5.0.0', - license='BSD', + license='BSD-2-Clause', description='Container class boilerplate killer.', long_description='%s\n%s' % ( re.compile('^.. start-badges.*^.. end-badges', re.M | re.S).sub('', read('README.rst')), @@ -48,20 +49,29 @@ 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 :: 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-fields.readthedocs.io/', + 'Changelog': 'https://python-fields.readthedocs.io/en/latest/changelog.html', + 'Issue Tracker': 'https://github.com/ionelmc/python-fields/issues', + }, keywords=[ "container", "fields", "object", "class", "boilerplate" ], + python_requires='>=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*', install_requires=[ # eg: "aspectlib==1.1.1", "six>=1.7", ], diff --git a/tox.ini b/tox.ini index eb07c1a..cbbf3c4 100644 --- a/tox.ini +++ b/tox.ini @@ -1,49 +1,68 @@ -; a generative tox configuration, see: https://testrun.org/tox/latest/config.html#generative-envlist +[testenv:bootstrap] +deps = + jinja2 + matrix + 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, - {py27,py33,py34,py35,py36,pypy}-{cover,nocov}, - report, - docs + docs, + {py27,py34,py35,py36,py37,pypy,pypy3}-{cover,nocov}, + report +ignore_basepython_conflict = true [testenv] basepython = pypy: {env:TOXPYTHON:pypy} - {py27,docs,spell}: {env:TOXPYTHON:python2.7} - py33: {env:TOXPYTHON:python3.3} + pypy3: {env:TOXPYTHON:pypy3} + py27: {env:TOXPYTHON:python2.7} py34: {env:TOXPYTHON:python3.4} py35: {env:TOXPYTHON:python3.5} - py36: {env:TOXPYTHON:python3.6} - {bootstrap,clean,check,report,coveralls,codecov}: python3 + {py36,docs,spell}: {env:TOXPYTHON:python3.6} + py37: {env:TOXPYTHON:python3.7} + {bootstrap,clean,check,report,codecov,coveralls}: {env:TOXPYTHON:python3} setenv = PYTHONPATH={toxinidir}/tests PYTHONUNBUFFERED=yes passenv = * -usedevelop = false +usedevelop = + cover: true + nocov: false deps = pytest pytest-travis-fold - pytest-benchmark==3.1a1 + pytest-benchmark==3.2.2 characteristic==14.3.0 - attrs==16.3.0 + attrs==19.2.0 {py34,py35,py36}: cnamedtuple==0.1.6 cover: pytest-cov commands = - nocov: {posargs:py.test -vv --ignore=src} - cover: {posargs:py.test --cov --cov-report=term-missing -vv} + nocov: {posargs:pytest -vv --ignore=src} + cover: {posargs:pytest --cov --cov-report=term-missing -vv} -[testenv:bootstrap] +[testenv:check] deps = - jinja2 - matrix + docutils + check-manifest + flake8 + readme-renderer + pygments + isort skip_install = true commands = - python ci/bootstrap.py -passenv = - * + 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 [testenv:spell] setenv = @@ -57,27 +76,13 @@ deps = pyenchant [testenv: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: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 src tests setup.py - isort --verbose --check-only --diff --recursive src tests setup.py - [testenv:coveralls] deps = coveralls @@ -90,15 +95,12 @@ deps = codecov skip_install = true commands = - coverage xml --ignore-errors codecov [] - [testenv:report] deps = coverage skip_install = true commands = - coverage combine --append coverage report coverage html @@ -106,18 +108,3 @@ commands = commands = coverage erase skip_install = true deps = coverage - -[testenv:py27-cover] -usedevelop = true - -[testenv:py33-cover] -usedevelop = true - -[testenv:py34-cover] -usedevelop = true - -[testenv:py35-cover] -usedevelop = true - -[testenv:pypy-cover] -usedevelop = true From 73436da741199851f59757394424b1b3f5f5ff03 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ionel=20Cristian=20M=C4=83rie=C8=99?= Date: Sat, 5 Oct 2019 02:48:27 +0300 Subject: [PATCH 13/16] Fix readme. --- README.rst | 37 ++++++++++--------------------------- 1 file changed, 10 insertions(+), 27 deletions(-) diff --git a/README.rst b/README.rst index 3a61416..55c2791 100644 --- a/README.rst +++ b/README.rst @@ -12,16 +12,14 @@ Overview * - tests - | |travis| |appveyor| |requires| | |coveralls| |codecov| - | |landscape| |scrutinizer| |codacy| |codeclimate| * - package - | |version| |wheel| |supported-versions| |supported-implementations| | |commits-since| - .. |docs| image:: https://readthedocs.org/projects/python-fields/badge/?style=flat :target: https://readthedocs.org/projects/python-fields :alt: Documentation Status -.. |travis| image:: https://travis-ci.org/ionelmc/python-fields.svg?branch=master +.. |travis| image:: https://api.travis-ci.org/ionelmc/python-fields.svg?branch=master :alt: Travis-CI Build Status :target: https://travis-ci.org/ionelmc/python-fields @@ -41,48 +39,33 @@ Overview :alt: Coverage Status :target: https://codecov.io/github/ionelmc/python-fields -.. |landscape| image:: https://landscape.io/github/ionelmc/python-fields/master/landscape.svg?style=flat - :target: https://landscape.io/github/ionelmc/python-fields/master - :alt: Code Quality Status - -.. |codacy| image:: https://img.shields.io/codacy/REPLACE_WITH_PROJECT_ID.svg?style=flat - :target: https://www.codacy.com/app/ionelmc/python-fields - :alt: Codacy Code Quality Status - -.. |codeclimate| image:: https://codeclimate.com/github/ionelmc/python-fields/badges/gpa.svg - :target: https://codeclimate.com/github/ionelmc/python-fields - :alt: CodeClimate Quality Status - .. |version| image:: https://img.shields.io/pypi/v/fields.svg :alt: PyPI Package latest release - :target: https://pypi.python.org/pypi/fields - -.. |commits-since| image:: https://img.shields.io/github/commits-since/ionelmc/python-fields/v5.0.0.svg - :alt: Commits since latest release - :target: https://github.com/ionelmc/python-fields/compare/v5.0.0...master + :target: https://pypi.org/project/fields .. |wheel| image:: https://img.shields.io/pypi/wheel/fields.svg :alt: PyPI Wheel - :target: https://pypi.python.org/pypi/fields + :target: https://pypi.org/project/fields .. |supported-versions| image:: https://img.shields.io/pypi/pyversions/fields.svg :alt: Supported versions - :target: https://pypi.python.org/pypi/fields + :target: https://pypi.org/project/fields .. |supported-implementations| image:: https://img.shields.io/pypi/implementation/fields.svg :alt: Supported implementations - :target: https://pypi.python.org/pypi/fields + :target: https://pypi.org/project/fields + +.. |commits-since| image:: https://img.shields.io/github/commits-since/ionelmc/python-fields/v5.0.0.svg + :alt: Commits since latest release + :target: https://github.com/ionelmc/python-fields/compare/v5.0.0...master -.. |scrutinizer| image:: https://img.shields.io/scrutinizer/g/ionelmc/python-fields/master.svg - :alt: Scrutinizer Status - :target: https://scrutinizer-ci.com/g/ionelmc/python-fields/ .. end-badges Container class boilerplate killer. -* Free software: BSD license +* Free software: BSD 2-Clause License Installation ============ From 8e6470a7b7554dfd0d3c91ad5416ff7cfb98ddb8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ionel=20Cristian=20M=C4=83rie=C8=99?= Date: Sat, 5 Oct 2019 12:09:07 +0300 Subject: [PATCH 14/16] Style fixes. --- MANIFEST.in | 2 +- README.rst | 4 ++-- src/fields/__init__.py | 3 +-- tests/test_perf.py | 7 ++++--- 4 files changed, 8 insertions(+), 8 deletions(-) diff --git a/MANIFEST.in b/MANIFEST.in index 842b2e4..e919e3f 100644 --- a/MANIFEST.in +++ b/MANIFEST.in @@ -16,7 +16,7 @@ include CONTRIBUTING.rst include LICENSE include README.rst -include tox.ini .travis.yml appveyor.yml +include tox.ini .travis.yml .appveyor.yml include conftest.py global-exclude *.py[cod] __pycache__ *.so *.dylib diff --git a/README.rst b/README.rst index 55c2791..ff5a0ab 100644 --- a/README.rst +++ b/README.rst @@ -344,7 +344,7 @@ but quite differently than ``fields``. Definitely worth considering. Also, nowdays it has more features than ``fields``. See `#6 `_. -.. _attrs: `_ + -- Isaac Dickinson .. diff --git a/src/fields/__init__.py b/src/fields/__init__.py index 5cff120..efeedaf 100644 --- a/src/fields/__init__.py +++ b/src/fields/__init__.py @@ -12,11 +12,10 @@ """ import linecache import sys +import zlib from itertools import chain from operator import itemgetter -import zlib - try: from collections import OrderedDict except ImportError: diff --git a/tests/test_perf.py b/tests/test_perf.py index 8073185..d7869ce 100644 --- a/tests/test_perf.py +++ b/tests/test_perf.py @@ -2,10 +2,10 @@ from functools import partial import pytest -from attr import ib as badly_named_field -from attr import s as badly_named_class_decorator from attr import Factory +from attr import ib as badly_named_field from attr import make_class +from attr import s as badly_named_class_decorator from characteristic import Attribute from characteristic import attributes @@ -82,7 +82,8 @@ def __init__(self, a, b, c="abc"): if cnamedtuple: cnamedtuple_class = cnamedtuple("namedtuple_class", ["a", "b", "c"]) else: - cnamedtuple_class = lambda *args, **kwargs: pytest.skip("Not available.") + def cnamedtuple_class(*args, **kwargs): + pytest.skip("Not available.") attrs_class = make_class("attrs_class", ["a", "b", "c"]) From b655c60e169fdcc12c00b8388bc1336a2b49017c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ionel=20Cristian=20M=C4=83rie=C8=99?= Date: Thu, 26 Aug 2021 15:56:26 +0300 Subject: [PATCH 15/16] Update skel and test deps. Ref #7. --- .appveyor.yml | 110 ++++++++++++++++++---------------- .bumpversion.cfg | 10 +++- .cookiecutterrc | 11 ++-- .editorconfig | 11 +++- .gitignore | 6 +- .pre-commit-config.yaml | 20 +++++++ .readthedocs.yml | 10 ++++ .travis.yml | 31 +++++----- CONTRIBUTING.rst | 11 ++-- LICENSE | 2 +- MANIFEST.in | 8 +-- README.rst | 8 +-- ci/appveyor-with-compiler.cmd | 23 ------- ci/bootstrap.py | 9 +-- ci/requirements.txt | 1 + ci/templates/.appveyor.yml | 27 ++++++--- ci/templates/.travis.yml | 9 +-- docs/conf.py | 11 +--- docs/index.rst | 1 - setup.cfg | 7 +-- setup.py | 6 +- tox.ini | 39 +++++------- 22 files changed, 197 insertions(+), 174 deletions(-) create mode 100644 .pre-commit-config.yaml create mode 100644 .readthedocs.yml delete mode 100644 ci/appveyor-with-compiler.cmd mode change 100644 => 100755 setup.py diff --git a/.appveyor.yml b/.appveyor.yml index d624429..14e41af 100644 --- a/.appveyor.yml +++ b/.appveyor.yml @@ -1,5 +1,20 @@ version: '{branch}-{build}' build: off +image: + - Visual Studio 2019 + - Visual Studio 2015 +matrix: + exclude: + - image: Visual Studio 2019 + PYTHON_VERSION: 2.7 + - image: Visual Studio 2015 + PYTHON_VERSION: '3.6' + - image: Visual Studio 2015 + PYTHON_VERSION: '3.7' + - image: Visual Studio 2015 + PYTHON_VERSION: '3.8' + - image: Visual Studio 2015 + PYTHON_VERSION: '3.9' environment: global: COVERALLS_EXTRAS: '-v' @@ -20,7 +35,6 @@ environment: PYTHON_HOME: C:\Python27-x64 PYTHON_VERSION: '2.7' PYTHON_ARCH: '64' - WINDOWS_SDK_VERSION: v7.0 - TOXENV: py27-nocov TOXPYTHON: C:\Python27\python.exe PYTHON_HOME: C:\Python27 @@ -33,53 +47,6 @@ environment: PYTHON_VERSION: '2.7' PYTHON_ARCH: '64' WHEEL_PATH: .tox/dist - WINDOWS_SDK_VERSION: v7.0 - - TOXENV: py34-cover,codecov,coveralls - TOXPYTHON: C:\Python34\python.exe - PYTHON_HOME: C:\Python34 - PYTHON_VERSION: '3.4' - PYTHON_ARCH: '32' - - TOXENV: py34-cover,codecov,coveralls - TOXPYTHON: C:\Python34-x64\python.exe - PYTHON_HOME: C:\Python34-x64 - PYTHON_VERSION: '3.4' - PYTHON_ARCH: '64' - WINDOWS_SDK_VERSION: v7.1 - - TOXENV: py34-nocov - TOXPYTHON: C:\Python34\python.exe - PYTHON_HOME: C:\Python34 - PYTHON_VERSION: '3.4' - PYTHON_ARCH: '32' - WHEEL_PATH: .tox/dist - - TOXENV: py34-nocov - TOXPYTHON: C:\Python34-x64\python.exe - PYTHON_HOME: C:\Python34-x64 - PYTHON_VERSION: '3.4' - PYTHON_ARCH: '64' - WHEEL_PATH: .tox/dist - WINDOWS_SDK_VERSION: v7.1 - - 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-nocov - TOXPYTHON: C:\Python35\python.exe - PYTHON_HOME: C:\Python35 - PYTHON_VERSION: '3.5' - PYTHON_ARCH: '32' - WHEEL_PATH: .tox/dist - - TOXENV: py35-nocov - 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 @@ -124,17 +91,60 @@ environment: 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-nocov + TOXPYTHON: C:\Python38\python.exe + PYTHON_HOME: C:\Python38 + PYTHON_VERSION: '3.8' + PYTHON_ARCH: '32' + WHEEL_PATH: .tox/dist + - TOXENV: py38-nocov + 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-nocov + TOXPYTHON: C:\Python39\python.exe + PYTHON_HOME: C:\Python39 + PYTHON_VERSION: '3.9' + PYTHON_ARCH: '32' + WHEEL_PATH: .tox/dist + - TOXENV: py39-nocov + 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 + - %PYTHON_HOME%\Scripts\tox on_failure: - ps: dir "env:" - ps: get-content .tox\*\log\* diff --git a/.bumpversion.cfg b/.bumpversion.cfg index 64b30c1..8575fa9 100644 --- a/.bumpversion.cfg +++ b/.bumpversion.cfg @@ -7,9 +7,13 @@ tag = True search = version='{current_version}' replace = version='{new_version}' -[bumpversion:file:README.rst] -search = v{current_version}. -replace = v{new_version}. +[bumpversion:file (badge):README.rst] +search = /v{current_version}.svg +replace = /v{new_version}.svg + +[bumpversion:file (link):README.rst] +search = /v{current_version}...master +replace = /v{new_version}...master [bumpversion:file:docs/conf.py] search = version = release = '{current_version}' diff --git a/.cookiecutterrc b/.cookiecutterrc index 7309bb6..0160103 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: '-' @@ -23,10 +20,11 @@ cookiecutter: distribution_name: fields email: contact@ionelmc.ro full_name: Ionel Cristian Mărieș - landscape: no + legacy_python: yes license: BSD 2-Clause License linter: flake8 package_name: fields + pre_commit: yes project_name: Fields project_short_description: Container class boilerplate killer. pypi_badge: yes @@ -50,6 +48,7 @@ cookiecutter: travis: yes travis_osx: no version: 5.0.0 + version_manager: bump2version website: https://blog.ionelmc.ro year_from: '2014' - year_to: '2019' + year_to: '2021' diff --git a/.editorconfig b/.editorconfig index 4000618..586c736 100644 --- a/.editorconfig +++ b/.editorconfig @@ -1,13 +1,20 @@ -# see http://editorconfig.org +# see https://editorconfig.org/ 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 [*.{bat,cmd,ps1}] end_of_line = crlf + +[*.{yml,yaml}] +indent_size = 2 + +[*.tsv] +indent_style = tab diff --git a/.gitignore b/.gitignore index 486405e..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 @@ -62,6 +65,7 @@ docs/_build .env .cache .pytest +.benchmarks .bootstrap .appveyor.token *.bak diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml new file mode 100644 index 0000000..6e974cb --- /dev/null +++ b/.pre-commit-config.yaml @@ -0,0 +1,20 @@ +# 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|\.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: + - 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 5e7ef9c..260e25a 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' @@ -19,18 +21,6 @@ matrix: - env: - TOXENV=py27-nocov python: '2.7' - - env: - - TOXENV=py34-cover,codecov,coveralls - python: '3.4' - - env: - - TOXENV=py34-nocov - python: '3.4' - - env: - - TOXENV=py35-cover,codecov,coveralls - python: '3.5' - - env: - - TOXENV=py35-nocov - python: '3.5' - env: - TOXENV=py36-cover,codecov,coveralls python: '3.6' @@ -43,6 +33,18 @@ matrix: - env: - TOXENV=py37-nocov python: '3.7' + - env: + - TOXENV=py38-cover,codecov,coveralls + python: '3.8' + - env: + - TOXENV=py38-nocov + python: '3.8' + - env: + - TOXENV=py39-cover,codecov,coveralls + python: '3.9' + - env: + - TOXENV=py39-nocov + python: '3.9' - env: - TOXENV=pypy-cover,codecov,coveralls python: 'pypy' @@ -64,14 +66,13 @@ before_install: 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: - - 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 3b12480..e11c7f0 100644 --- a/CONTRIBUTING.rst +++ b/CONTRIBUTING.rst @@ -41,7 +41,7 @@ To set up `python-fields` for local development: (look for the "Fork" button). 2. Clone your fork locally:: - git clone git@github.com:ionelmc/python-fields.git + git clone git@github.com:YOURGITHUBNAME/python-fields.git 3. Create a branch for local development:: @@ -49,7 +49,7 @@ To set up `python-fields` 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 @@ -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 ... @@ -85,6 +86,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/LICENSE b/LICENSE index 3871221..51ed2fa 100644 --- a/LICENSE +++ b/LICENSE @@ -1,6 +1,6 @@ BSD 2-Clause License -Copyright (c) 2014-2019, 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/MANIFEST.in b/MANIFEST.in index e919e3f..81abe92 100644 --- a/MANIFEST.in +++ b/MANIFEST.in @@ -1,14 +1,11 @@ graft docs -graft examples graft src graft ci graft tests - include .bumpversion.cfg include .coveragerc include .cookiecutterrc include .editorconfig -include .isort.cfg include AUTHORS.rst include CHANGELOG.rst @@ -16,7 +13,6 @@ include CONTRIBUTING.rst include LICENSE include README.rst -include tox.ini .travis.yml .appveyor.yml -include conftest.py +include tox.ini .travis.yml .appveyor.yml .readthedocs.yml .pre-commit-config.yaml -global-exclude *.py[cod] __pycache__ *.so *.dylib +global-exclude *.py[cod] __pycache__/* *.so *.dylib diff --git a/README.rst b/README.rst index ff5a0ab..dbc96e0 100644 --- a/README.rst +++ b/README.rst @@ -16,12 +16,12 @@ Overview - | |version| |wheel| |supported-versions| |supported-implementations| | |commits-since| .. |docs| image:: https://readthedocs.org/projects/python-fields/badge/?style=flat - :target: https://readthedocs.org/projects/python-fields + :target: https://python-fields.readthedocs.io/ :alt: Documentation Status -.. |travis| image:: https://api.travis-ci.org/ionelmc/python-fields.svg?branch=master +.. |travis| image:: https://api.travis-ci.com/ionelmc/python-fields.svg?branch=master :alt: Travis-CI Build Status - :target: https://travis-ci.org/ionelmc/python-fields + :target: https://travis-ci.com/github/ionelmc/python-fields .. |appveyor| image:: https://ci.appveyor.com/api/projects/status/github/ionelmc/python-fields?branch=master&svg=true :alt: AppVeyor Build Status @@ -35,7 +35,7 @@ Overview :alt: Coverage Status :target: https://coveralls.io/r/ionelmc/python-fields -.. |codecov| image:: https://codecov.io/github/ionelmc/python-fields/coverage.svg?branch=master +.. |codecov| image:: https://codecov.io/gh/ionelmc/python-fields/branch/master/graphs/badge.svg?branch=master :alt: Coverage Status :target: https://codecov.io/github/ionelmc/python-fields 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 7bdd523..2eb7723 100755 --- a/ci/bootstrap.py +++ b/ci/bootstrap.py @@ -12,9 +12,6 @@ from os.path import exists from os.path import join -import jinja2 - - base_path = dirname(dirname(abspath(__file__))) @@ -22,6 +19,7 @@ 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": @@ -49,7 +47,10 @@ def exec_in_env(): 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)) jinja = jinja2.Environment( @@ -76,6 +77,7 @@ def main(): print("Wrote {}".format(name)) print("DONE.") + if __name__ == "__main__": args = sys.argv[1:] if args == ["--no-env"]: @@ -85,4 +87,3 @@ def main(): else: print("Unexpected arguments {0}".format(args), file=sys.stderr) sys.exit(1) - diff --git a/ci/requirements.txt b/ci/requirements.txt index 1c8d385..d7f5177 100644 --- a/ci/requirements.txt +++ b/ci/requirements.txt @@ -1,3 +1,4 @@ virtualenv>=16.6.0 pip>=19.1.1 setuptools>=18.0.1 +six>=1.14.0 diff --git a/ci/templates/.appveyor.yml b/ci/templates/.appveyor.yml index 1c38167..f5fd8a8 100644 --- a/ci/templates/.appveyor.yml +++ b/ci/templates/.appveyor.yml @@ -1,5 +1,20 @@ version: '{branch}-{build}' build: off +image: + - Visual Studio 2019 + - Visual Studio 2015 +matrix: + exclude: + - image: Visual Studio 2019 + PYTHON_VERSION: 2.7 + - image: Visual Studio 2015 + PYTHON_VERSION: '3.6' + - image: Visual Studio 2015 + PYTHON_VERSION: '3.7' + - image: Visual Studio 2015 + PYTHON_VERSION: '3.8' + - image: Visual Studio 2015 + PYTHON_VERSION: '3.9' environment: global: COVERALLS_EXTRAS: '-v' @@ -12,8 +27,7 @@ environment: PYTHON_ARCH: '32' {% for env in tox_environments %} {% if env.startswith(('py2', 'py3')) %} - - TOXENV: {{ env }}{% if env.endswith('-cover') %},codecov,coveralls{% endif %} - + - TOXENV: {{ env }}{% if env.endswith('-cover') %},codecov,coveralls{% endif %}{{ "" }} TOXPYTHON: C:\Python{{ env[2:4] }}\python.exe PYTHON_HOME: C:\Python{{ env[2:4] }} PYTHON_VERSION: '{{ env[2] }}.{{ env[3] }}' @@ -21,8 +35,7 @@ environment: {% if 'nocov' in env %} WHEEL_PATH: .tox/dist {% endif %} - - TOXENV: {{ env }}{% if env.endswith('-cover') %},codecov,coveralls{% endif %} - + - TOXENV: {{ env }}{% if env.endswith('-cover') %},codecov,coveralls{% endif %}{{ "" }} TOXPYTHON: C:\Python{{ env[2:4] }}-x64\python.exe PYTHON_HOME: C:\Python{{ env[2:4] }}-x64 PYTHON_VERSION: '{{ env[2] }}.{{ env[3] }}' @@ -30,9 +43,6 @@ environment: {% if 'nocov' in env %} WHEEL_PATH: .tox/dist {% endif %} -{% if env.startswith(('py2', 'py34')) %} - WINDOWS_SDK_VERSION: v7.{{ '1' if env.startswith('py3') else '0' }} -{% endif %} {% endif %}{% endfor %} init: - ps: echo $env:TOXENV @@ -40,11 +50,10 @@ init: 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 + - %PYTHON_HOME%\Scripts\tox on_failure: - ps: dir "env:" - ps: get-content .tox\*\log\* diff --git a/ci/templates/.travis.yml b/ci/templates/.travis.yml index 57316b9..1a1c1ca 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' @@ -23,7 +25,7 @@ matrix: python: 'pypy' {%- else %}{{ '' }} python: '{{ '{0[2]}.{0[3]}'.format(env) }}' -{%- endif %} +{%- endif %}{{ '' }} {%- endfor %}{{ '' }} before_install: - python --version @@ -32,14 +34,13 @@ before_install: 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: - - more .tox/log/* | cat - - more .tox/*/log/* | cat + - cat .tox/log/* + - cat .tox/*/log/* notifications: email: on_success: never diff --git a/docs/conf.py b/docs/conf.py index d0b3c8a..a4abc50 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 = 'Fields' -year = '2014-2019' +year = '2014-2021' author = 'Ionel Cristian Mărieș' copyright = '{0}, {1}'.format(year, author) version = release = '5.0.0' @@ -34,7 +28,6 @@ 'issue': ('https://github.com/ionelmc/python-fields/issues/%s', '#'), 'pr': ('https://github.com/ionelmc/python-fields/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/docs/index.rst b/docs/index.rst index 40f35b5..ad842d5 100644 --- a/docs/index.rst +++ b/docs/index.rst @@ -19,4 +19,3 @@ Indices and tables * :ref:`genindex` * :ref:`modindex` * :ref:`search` - diff --git a/setup.cfg b/setup.cfg index de160b4..023e944 100644 --- a/setup.cfg +++ b/setup.cfg @@ -3,7 +3,7 @@ universal = 1 [flake8] max-line-length = 140 -exclude = */migrations/* +exclude = .tox,.eggs,ci/templates,build,dist [tool:pytest] # If a pytest section is found in one of the possible config files @@ -24,7 +24,7 @@ python_files = tests.py addopts = -ra - --strict + --strict-markers --ignore=docs/conf.py --ignore=setup.py --ignore=ci @@ -42,5 +42,4 @@ line_length = 120 known_first_party = fields default_section = THIRDPARTY forced_separate = test_fields -not_skip = __init__.py -skip = migrations +skip = .tox,.eggs,ci/templates,build,dist diff --git a/setup.py b/setup.py old mode 100644 new mode 100755 index 3dfb17d..ef5e94b --- a/setup.py +++ b/setup.py @@ -51,10 +51,10 @@ def read(*names, **kwargs): 'Programming Language :: Python', 'Programming Language :: Python :: 2.7', 'Programming Language :: Python :: 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 :: 3.9', 'Programming Language :: Python :: Implementation :: CPython', 'Programming Language :: Python :: Implementation :: PyPy', # uncomment if you test on these interpreters: @@ -71,7 +71,7 @@ def read(*names, **kwargs): keywords=[ "container", "fields", "object", "class", "boilerplate" ], - python_requires='>=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*', + python_requires='>=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*, !=3.5.*', install_requires=[ # eg: "aspectlib==1.1.1", "six>=1.7", ], diff --git a/tox.ini b/tox.ini index cbbf3c4..95c48ee 100644 --- a/tox.ini +++ b/tox.ini @@ -1,7 +1,6 @@ [testenv:bootstrap] deps = jinja2 - matrix tox skip_install = true commands = @@ -15,7 +14,7 @@ envlist = clean, check, docs, - {py27,py34,py35,py36,py37,pypy,pypy3}-{cover,nocov}, + {py27,py36,py37,py38,py39,pypy,pypy3}-{cover,nocov}, report ignore_basepython_conflict = true @@ -24,11 +23,11 @@ basepython = pypy: {env:TOXPYTHON:pypy} pypy3: {env:TOXPYTHON:pypy3} py27: {env:TOXPYTHON:python2.7} - py34: {env:TOXPYTHON:python3.4} - py35: {env:TOXPYTHON:python3.5} - {py36,docs,spell}: {env:TOXPYTHON:python3.6} + py36: {env:TOXPYTHON:python3.6} py37: {env:TOXPYTHON:python3.7} - {bootstrap,clean,check,report,codecov,coveralls}: {env:TOXPYTHON:python3} + py38: {env:TOXPYTHON:python3.8} + py39: {env:TOXPYTHON:python3.9} + {bootstrap,clean,check,report,docs,codecov,coveralls}: {env:TOXPYTHON:python3} setenv = PYTHONPATH={toxinidir}/tests PYTHONUNBUFFERED=yes @@ -40,10 +39,11 @@ usedevelop = deps = pytest pytest-travis-fold - pytest-benchmark==3.2.2 + pytest-benchmark + characteristic==14.3.0 - attrs==19.2.0 - {py34,py35,py36}: cnamedtuple==0.1.6 + attrs==21.2.0 + {py36,py37,py38,py39}: cnamedtuple==0.1.6 cover: pytest-cov commands = nocov: {posargs:pytest -vv --ignore=src} @@ -61,19 +61,8 @@ skip_install = true 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 - -[testenv:spell] -setenv = - SPELLCHECK=1 -commands = - sphinx-build -b spelling docs dist/docs -skip_install = true -deps = - -r{toxinidir}/docs/requirements.txt - sphinxcontrib-spelling - pyenchant + flake8 + isort --verbose --check-only --diff --filter-files . [testenv:docs] usedevelop = true @@ -98,7 +87,8 @@ commands = codecov [] [testenv:report] -deps = coverage +deps = + coverage skip_install = true commands = coverage report @@ -107,4 +97,5 @@ commands = [testenv:clean] commands = coverage erase skip_install = true -deps = coverage +deps = + coverage From f0e1acafe0107563bd5d3e3c1761c5ffcfa8068a Mon Sep 17 00:00:00 2001 From: Tim Gates Date: Wed, 10 Nov 2021 06:16:08 +1100 Subject: [PATCH 16/16] docs: Fix a few typos There are small typos in: - README.rst - src/fields/__init__.py - tests/test_fields.py Fixes: - Should read `repetitive` rather than `repetivive`. - Should read `nowadays` rather than `nowdays`. - Should read `inflexible` rather than `unflexible`. - Should read `different` rather than `differnt`. - Should read `construction` rather than `contruction`. - Should read `completely` rather than `completey`. --- README.rst | 6 +++--- src/fields/__init__.py | 2 +- tests/test_fields.py | 2 +- 3 files changed, 5 insertions(+), 5 deletions(-) diff --git a/README.rst b/README.rst index dbc96e0..2261e19 100644 --- a/README.rst +++ b/README.rst @@ -229,7 +229,7 @@ Yes, ofcourse. Why not ``namedtuple``? ------------------------ -It's ugly, repetivive and unflexible. Compare this: +It's ugly, repetitive and inflexible. Compare this: .. code:: python @@ -342,7 +342,7 @@ Worth noting: All in all, attrs_ is a fast and minimal container library that does support subclasses, but quite differently than ``fields``. Definitely worth considering. -Also, nowdays it has more features than ``fields``. See `#6 `_. +Also, nowadays it has more features than ``fields``. See `#6 `_. .. _attrs: https://pypi.python.org/pypi/attrs @@ -375,7 +375,7 @@ Testimonials .. - Fields is completey bat-shit insane, but kind of cool. + Fields is completely bat-shit insane, but kind of cool. -- Someone on IRC (#python) diff --git a/src/fields/__init__.py b/src/fields/__init__.py index efeedaf..5cf8b44 100644 --- a/src/fields/__init__.py +++ b/src/fields/__init__.py @@ -5,7 +5,7 @@ and default values). * The `factory`. A metaclass that implements attribute/item access, so you can do ``Fields.a.b.c``. On each getattr/getitem it returns a new instance with the new state. Its ``__new__`` method takes extra arguments to store - the contruction state and it works in two ways: + the construction state and it works in two ways: * Construction phase (there are no bases). Make new instances of the `Factory` with new state. * Usage phase. When subclassed (there are bases) it will use the sealer to return the final class. diff --git a/tests/test_fields.py b/tests/test_fields.py index 01b4f36..47e7ad2 100644 --- a/tests/test_fields.py +++ b/tests/test_fields.py @@ -258,7 +258,7 @@ def test_unequal_same_class(CmpC): def test_unequal_different_class(CmpC): """ - Unequal objects of differnt type are detected even if their attributes + Unequal objects of different type are detected even if their attributes match. """ class NotCmpC(object):