Remove Cython & Move to pyproject.toml (#4473)

* Remove Cython

* fix CI

* fix coverage

* fix tests

* switching to pypyroject.toml

* pre-commit all and use pre-commit for linting

* no mypy tests on macos and windows on ci, use flake8-pyproject

* fix docs and tests CI

* check build is working

* drop pytest-cov

* window and macos ci with 3.11, reduce filtering

* use pip-tools to pin all dependencies

* fix docs and fastapi tests

* fix test deps for 3.7

* no cache on tests job

* revert fastapi changes, fix coverage

* fix mypy coverage

* test with older mypy

* dotenv not required for mypy tests

* split testing requirements std and extra

* typo

* @PrettyWood comments

* correct branch name

* mypy python_version and pr template
This commit is contained in:
Samuel Colvin
2022-09-06 17:15:51 +01:00
committed by GitHub
parent bc74342b39
commit f341049b9e
58 changed files with 787 additions and 970 deletions
+3 -3
View File
@@ -13,7 +13,7 @@ body:
label: Initial Checks
description: |
Just a few checks to make sure you need to create a bug report.
_Sorry to sound so draconian 👿; but every second spent replying to issues is time not spent improving pydantic 🙇._
options:
- label: I have searched GitHub for a duplicate issue and I'm sure this is something new
@@ -23,7 +23,7 @@ body:
- label: I have read and followed [the docs](https://pydantic-docs.helpmanual.io) and still think this is a bug
required: true
- label: >
I am confident that the issue is with pydantic
I am confident that the issue is with pydantic
(not my code, or another library in the ecosystem like [FastAPI](https://fastapi.tiangolo.com) or
[mypy](https://mypy.readthedocs.io/en/stable))
required: true
@@ -60,7 +60,7 @@ body:
label: Python, Pydantic & OS Version
description: |
Which version of Python & Pydantic are you using, and which Operating System?
Please run the following command and copy the output below:
```bash
+2 -2
View File
@@ -13,7 +13,7 @@ body:
label: Initial Checks
description: |
Just a few checks to make sure you need to create a feature request.
_Sorry to sound so draconian 👿; but every second spent replying to issues is time not spent improving pydantic 🙇._
options:
- label: I have searched Google & GitHub for similar requests and couldn't find anything
@@ -27,7 +27,7 @@ body:
label: Description
description: |
Please give as much detail as possible about the feature you would like to suggest. 🙏
You might like to add:
* A demo of how code might look when using the feature
* Your use case(s) for the feature
+8 -17
View File
@@ -1,23 +1,14 @@
<!-- Thank you for your contribution! -->
<!-- Unless your change is trivial, please create an issue to discuss the change before creating a PR -->
<!-- See https://pydantic-docs.helpmanual.io/contributing/ for help on Contributing -->
<!-- PLEASE DO **NOT** put issue ids in the PR title! Instead, add a descriptive title and put ids in the body -->
# WARNING!!!
## Change Summary
We're currently in the process of rewriting pydantic in preparation for V2, see
https://pydantic-docs.helpmanual.io/blog/pydantic-v2/.
<!-- Please give a short summary of the changes. -->
As a result, much of the codebase will change significantly over the coming months.
## Related issue number
To avoid wasting your time, please only create Pull Requests if you've got explicit approval by a maintainer.
<!-- Are there any issues opened that will be resolved by merging this change? -->
<!-- WARNING: please use "fix #123" style references so the issue is closed when this PR is merged. -->
Otherwise, your pull requests may be closed without review.
## Checklist
Thank you for your interest in pydantic, and your patience. :pray:
* [ ] Unit tests for the changes exist
* [ ] Tests pass on CI and coverage remains at 100%
* [ ] Documentation reflects the changes where applicable
* [ ] `changes/<pull request or issue id>-<github username>.md` file added describing change
(see [changes/README.md](https://github.com/pydantic/pydantic/blob/main/changes/README.md) for details.
You can [skip this check](https://github.com/pydantic/hooky#change-file-checks) if the change does not need a change file.)
* [ ] My PR is ready to review, **please add a comment including the phrase "please review" to assign reviewers**
**Note:** if you're making a pull request to fix pydantic v1.10, please make it against the `1.10.X-fixes` branch.
-5
View File
@@ -1,11 +1,6 @@
version: 2
updates:
- package-ecosystem: pip
directory: /
schedule:
interval: monthly
- package-ecosystem: github-actions
directory: /
schedule:
+70 -202
View File
@@ -30,29 +30,19 @@ jobs:
lint
${{ runner.os }}
${{ env.pythonLocation }}
${{ hashFiles('tests/requirements-linting.txt') }}
${{ hashFiles('requirements/linting.txt') }}
- name: install
if: steps.cache.outputs.cache-hit != 'true'
run: |
make install-linting
pip freeze
run: pip install -r requirements/linting.txt
- name: lint
run: make lint
- name: pyupgrade
run: make pyupgrade
- name: mypy
run: make mypy
- uses: pre-commit/action@v3.0.0
with:
extra_args: --all-files
- name: make history
run: python3 ./changes/make_history.py
- name: check dist
run: make check-dist
- name: install node for pyright
uses: actions/setup-node@v3
with:
@@ -80,13 +70,12 @@ jobs:
docs-build
${{ runner.os }}
${{ env.pythonLocation }}
${{ hashFiles('setup.py') }}
${{ hashFiles('requirements.txt') }}
${{ hashFiles('docs/requirements.txt') }}
${{ hashFiles('requirements/pyproject-all.txt') }}
${{ hashFiles('requirements/docs.txt') }}
- name: install
if: steps.cache.outputs.cache-hit != 'true'
run: make install-docs
run: pip install -r requirements/pyproject-all.txt -r requirements/docs.txt .
- name: build site
run: make docs
@@ -97,75 +86,17 @@ jobs:
name: docs
path: site
test-linux-compiled:
name: test py${{ matrix.python-version }} on linux compiled
runs-on: ubuntu-latest
strategy:
fail-fast: false
matrix:
python-version: ['3.7', '3.8', '3.9', '3.10', '3.11.0-rc.1']
env:
PYTHON: ${{ matrix.python-version }}
OS: ubuntu
steps:
- uses: actions/checkout@v3
- name: set up python
uses: actions/setup-python@v4
with:
python-version: ${{ matrix.python-version }}
- uses: actions/cache@v3
id: cache
with:
path: ${{ env.pythonLocation }}
key: >
test-linux-compiled
${{ runner.os }}
${{ env.pythonLocation }}
${{ hashFiles('setup.py') }}
${{ hashFiles('requirements.txt') }}
${{ hashFiles('tests/requirements-testing.txt') }}
- name: install
run: make install-testing
- name: compile
run: |
make build-trace
python -c "import sys, pydantic; print('compiled:', pydantic.compiled); sys.exit(0 if pydantic.compiled else 1)"
ls -alh
ls -alh pydantic/
- run: mkdir coverage
- name: test
run: make test
env:
COVERAGE_FILE: coverage/.coverage.linux-py${{ matrix.python-version }}-compiled
CONTEXT: linux-py${{ matrix.python-version }}-compiled
- name: store coverage files
uses: actions/upload-artifact@v3
with:
name: coverage
path: coverage
test-not-compiled:
test:
name: test py${{ matrix.python-version }} on ${{ matrix.os }}
strategy:
fail-fast: false
matrix:
os: [ubuntu, macos, windows]
python-version: ['3.7', '3.8', '3.9', '3.10']
include:
- os: ubuntu
python-version: ['3.7', '3.8', '3.9', '3.10', '3.11.0-rc.1']
env:
PYTHON: ${{ matrix.python-version }}
OS: ${{ matrix.os }}
COMPILED: no
DEPS: yes
runs-on: ${{ matrix.os }}-latest
@@ -177,40 +108,31 @@ jobs:
with:
python-version: ${{ matrix.python-version }}
- uses: actions/cache@v3
id: cache
with:
path: ${{ env.pythonLocation }}
key: >
test-not-compiled
${{ runner.os }}
${{ env.pythonLocation }}
${{ hashFiles('setup.py') }}
${{ hashFiles('requirements.txt') }}
${{ hashFiles('tests/requirements-testing.txt') }}
- name: install min deps
run: pip install -r requirements/pyproject-min.txt -r requirements/testing.txt
- name: install
run: make install-testing
- name: install pydantic
run: pip install .
- run: pip freeze
- run: mkdir coverage
- name: test with deps
run: make test
env:
COVERAGE_FILE: coverage/.coverage.${{ runner.os }}-py${{ matrix.python-version }}-with-deps
CONTEXT: ${{ runner.os }}-py${{ matrix.python-version }}-with-deps
- name: uninstall deps
run: pip uninstall -y cython email-validator devtools python-dotenv
- name: test without deps
run: make test
env:
COVERAGE_FILE: coverage/.coverage.${{ runner.os }}-py${{ matrix.python-version }}-without-deps
CONTEXT: ${{ runner.os }}-py${{ matrix.python-version }}-without-deps
- name: install extra deps
run: pip install -r requirements/pyproject-all.txt -r requirements/testing-extra.txt
- name: test with deps
run: make test
env:
COVERAGE_FILE: coverage/.coverage.${{ runner.os }}-py${{ matrix.python-version }}-with-deps
CONTEXT: ${{ runner.os }}-py${{ matrix.python-version }}-with-deps
- name: store coverage files
uses: actions/upload-artifact@v3
with:
@@ -223,7 +145,7 @@ jobs:
strategy:
fail-fast: false
matrix:
mypy-version: ['0.910', '0.921', '0.931', '0.942', '0.950', '0.960']
mypy-version: ['0.930', '0.942', '0.950', '0.961']
steps:
- uses: actions/checkout@v3
@@ -233,21 +155,30 @@ jobs:
with:
python-version: '3.10'
- uses: actions/cache@v3
id: cache
with:
path: ${{ env.pythonLocation }}
key: >
test-mypy
${{ runner.os }}
${{ env.pythonLocation }}
${{ hashFiles('requirements/pyproject-min.txt') }}
${{ hashFiles('requirements/testing.txt') }}
${{ matrix.mypy-version }}
- name: install
run: |
make install-testing
pip freeze
if: steps.cache.outputs.cache-hit != 'true'
run: pip install -r requirements/pyproject-min.txt -r requirements/testing.txt
- name: uninstall deps
run: pip uninstall -y mypy tomli toml
- name: install specific mypy version
- name: install mypy
if: steps.cache.outputs.cache-hit != 'true'
run: pip install mypy==${{ matrix.mypy-version }}
- run: mkdir coverage
- name: run tests
run: pytest --cov=pydantic tests/mypy
run: coverage run -m pytest tests/mypy
env:
COVERAGE_FILE: coverage/.coverage.linux-py3.10-mypy${{ matrix.mypy-version }}
CONTEXT: linux-py3.10-mypy${{ matrix.mypy-version }}
@@ -259,7 +190,7 @@ jobs:
path: coverage
coverage-combine:
needs: [test-linux-compiled, test-not-compiled, test-old-mypy]
needs: [test, test-old-mypy]
runs-on: ubuntu-latest
steps:
@@ -275,7 +206,7 @@ jobs:
name: coverage
path: coverage
- run: pip install coverage
- run: pip install coverage[toml]
- run: ls -la coverage
- run: coverage combine coverage
@@ -288,87 +219,29 @@ jobs:
name: coverage-html
path: htmlcov
test-fastapi:
name: test fastAPI
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- name: set up python
uses: actions/setup-python@v4
with:
python-version: '3.10'
- name: install
run: make install-testing
- name: test
run: make test-fastapi
build:
name: build py3.${{ matrix.python-version }} on ${{ matrix.platform || matrix.os }}
needs: [lint, test-linux-compiled, test-not-compiled, test-old-mypy, test-fastapi]
if: "success() && (startsWith(github.ref, 'refs/tags/') || github.ref == 'refs/heads/main')"
strategy:
fail-fast: false
matrix:
os: [ubuntu , macos , windows]
python-version: ['7', '8', '9', '10', '11']
include:
- os: ubuntu
platform: linux
- os: windows
ls: dir
runs-on: ${{ matrix.os }}-latest
steps:
- uses: actions/checkout@v3
- name: set up python
uses: actions/setup-python@v4
with:
python-version: '3.8'
- name: install
run: pip install -U twine setuptools wheel cibuildwheel
- name: build sdist
if: matrix.os == 'ubuntu' && matrix.python-version == '9'
run: python setup.py sdist bdist_wheel
env:
SKIP_CYTHON: 1
- name: build ${{ matrix.platform || matrix.os }} binaries
run: cibuildwheel --output-dir dist
env:
PIP: 'pip'
CIBW_BUILD: 'cp3${{ matrix.python-version }}-*'
CIBW_SKIP: '*-win32'
CIBW_PLATFORM: '${{ matrix.platform || matrix.os }}'
CIBW_BEFORE_BUILD: 'pip install -U cython'
CIBW_TEST_REQUIRES: 'pytest==6.2.5 pytest-mock==3.6.1'
CIBW_TEST_COMMAND: 'pytest {project}/tests'
CIBW_MANYLINUX_X86_64_IMAGE: 'manylinux2014'
CIBW_MANYLINUX_I686_IMAGE: 'manylinux2014'
CIBW_ARCHS_MACOS: 'x86_64 arm64'
CIBW_TEST_SKIP: '*-macosx_arm64' # see https://cibuildwheel.readthedocs.io/en/stable/faq/#universal2
# TODO build windows 32bit binaries
- name: list dist files
run: |
${{ matrix.ls || 'ls -lh' }} dist/
twine check dist/*
- name: Store dist artifacts
uses: actions/upload-artifact@v3
with:
name: pypi_files
path: dist
# FastAPI has a version constraint of pydantic<2.0.0, so we can't run tests, we expect them to break for now anyway
# test-fastapi:
# name: test fastAPI
# runs-on: ubuntu-latest
# steps:
# - uses: actions/checkout@v3
#
# - name: set up python
# uses: actions/setup-python@v4
# with:
# python-version: '3.10'
#
# - name: install
# run: |
# pip install -r requirements/pyproject-all.txt
# pip install .
#
# - name: test
# run: make test-fastapi
deploy:
name: Deploy
needs: build
needs: [lint, docs-build, test, test-old-mypy] # TODO re-add test-fastapi once fixed
if: "success() && startsWith(github.ref, 'refs/tags/')"
runs-on: ubuntu-latest
@@ -380,12 +253,6 @@ jobs:
with:
python-version: '3.10'
- name: get dist artifacts
uses: actions/download-artifact@v3
with:
name: pypi_files
path: dist
- name: get docs
uses: actions/download-artifact@v3
with:
@@ -393,17 +260,18 @@ jobs:
path: site
- name: install
run: pip install -U twine packaging
- name: twine check
run: |
twine check dist/*
ls -lh dist
run: pip install -U twine build packaging
- name: check tag
id: check-tag
run: ./tests/check_tag.py
- name: build
run: python -m build
- run: ls -lh dist
- run: twine check dist/*
- name: upload to pypi
run: twine upload dist/*
env:
-137
View File
@@ -1,137 +0,0 @@
# from https://github.com/hrvey/combine-prs-workflow/blob/master/combine-prs.yml
name: 'Combine Dependabot PRs'
on:
workflow_dispatch:
inputs:
branchPrefix:
description: 'Branch prefix to find combinable PRs based on'
required: true
default: 'dependabot/'
mustBeGreen:
description: 'Only combine PRs that are green'
required: true
default: true
combineBranchName:
description: 'Name of the branch to combine PRs into'
required: true
default: 'combine-dependabot-bumps'
ignoreLabel:
description: 'Exclude PRs with this label'
required: true
default: 'nocombine'
jobs:
combine-prs:
runs-on: ubuntu-latest
steps:
- uses: actions/github-script@v6
id: fetch-branch-names
name: Fetch branch names
with:
github-token: ${{secrets.GITHUB_TOKEN}}
script: |
const pulls = await github.paginate('GET /repos/:owner/:repo/pulls', {
owner: context.repo.owner,
repo: context.repo.repo
});
branches = [];
prs = [];
base_branch = null;
for (const pull of pulls) {
const branch = pull['head']['ref'];
console.log('Pull for branch: ' + branch);
if (branch.startsWith('${{ github.event.inputs.branchPrefix }}')) {
console.log('Branch matched: ' + branch);
statusOK = true;
if(${{ github.event.inputs.mustBeGreen }}) {
console.log('Checking green status: ' + branch);
const statuses = await github.paginate('GET /repos/{owner}/{repo}/commits/{ref}/status', {
owner: context.repo.owner,
repo: context.repo.repo,
ref: branch
});
if(statuses.length > 0) {
const latest_status = statuses[0]['state'];
console.log('Validating status: ' + latest_status);
if(latest_status != 'success') {
console.log('Discarding ' + branch + ' with status ' + latest_status);
statusOK = false;
}
}
}
console.log('Checking labels: ' + branch);
const labels = pull['labels'];
for(const label of labels) {
const labelName = label['name'];
console.log('Checking label: ' + labelName);
if(labelName == '${{ github.event.inputs.ignoreLabel }}') {
console.log('Discarding ' + branch + ' with label ' + labelName);
statusOK = false;
}
}
if (statusOK) {
console.log('Adding branch to array: ' + branch);
branches.push(branch);
prs.push('#' + pull['number'] + ' ' + pull['title']);
base_branch = pull['base']['ref'];
}
}
}
if (branches.length == 0) {
core.setFailed('No PRs/branches matched criteria');
return;
}
core.setOutput('base-branch', base_branch);
core.setOutput('prs-string', prs.join('\n'));
combined = branches.join(' ')
console.log('Combined: ' + combined);
return combined
- uses: actions/checkout@v3
with:
fetch-depth: 0
# Creates a branch with other PR branches merged together
- name: Created combined branch
env:
BASE_BRANCH: ${{ steps.fetch-branch-names.outputs.base-branch }}
BRANCHES_TO_COMBINE: ${{ steps.fetch-branch-names.outputs.result }}
COMBINE_BRANCH_NAME: ${{ github.event.inputs.combineBranchName }}
run: |
echo "$BRANCHES_TO_COMBINE"
sourcebranches="${BRANCHES_TO_COMBINE%\"}"
sourcebranches="${sourcebranches#\"}"
basebranch="${BASE_BRANCH%\"}"
basebranch="${basebranch#\"}"
git config pull.rebase false
git config user.name github-actions
git config user.email github-actions@github.com
git branch $COMBINE_BRANCH_NAME $basebranch
git checkout $COMBINE_BRANCH_NAME
git pull origin $sourcebranches --no-edit
git push origin $COMBINE_BRANCH_NAME
# Creates a PR with the new combined branch
- uses: actions/github-script@v6
name: Create Combined Pull Request
env:
PRS_STRING: ${{ steps.fetch-branch-names.outputs.prs-string }}
with:
github-token: ${{secrets.GITHUB_TOKEN}}
script: |
const prString = process.env.PRS_STRING;
const body = 'This PR was created by the Combine PRs action by combining the following PRs:\n' + prString;
await github.pulls.create({
owner: context.repo.owner,
repo: context.repo.repo,
title: 'Combined Dependabot Bumps',
head: '${{ github.event.inputs.combineBranchName }}',
base: '${{ steps.fetch-branch-names.outputs.base-branch }}',
body: body
});
+7 -2
View File
@@ -1,10 +1,12 @@
repos:
- repo: https://github.com/pre-commit/pre-commit-hooks
rev: v4.0.1
rev: v4.3.0
hooks:
- id: check-yaml
args: ['--unsafe']
- id: check-toml
- id: end-of-file-fixer
- id: trailing-whitespace
- repo: local
hooks:
@@ -13,13 +15,16 @@ repos:
entry: make lint
types: [python]
language: system
pass_filenames: false
- id: mypy
name: Mypy
entry: make mypy
types: [python]
language: system
pass_filenames: false
- id: pyupgrade
name: Pyupgrade
entry: make pyupgrade
entry: pyupgrade --py37-plus
types: [python]
language: system
exclude: ^docs/.*$
+32 -32
View File
@@ -1,8 +1,8 @@
## v1.10.2 (2022-09-05)
* **Revert Change:** Revert percent encoding of URL parts which was originally added in #4224, #4470 by @samuelcolvin
* Prevent long (length > `4_300`) strings/bytes as input to int fields, see
[python/cpython#95778](https://github.com/python/cpython/issues/95778) and
* Prevent long (length > `4_300`) strings/bytes as input to int fields, see
[python/cpython#95778](https://github.com/python/cpython/issues/95778) and
[CVE-2020-10735](https://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-2020-10735), #1477 by @samuelcolvin
* fix: dataclass wrapper was not always called, #4477 by @PrettyWood
* Use `tomllib` on Python 3.11 when parsing `mypy` configuration, #4476 by @hauntsaninja
@@ -40,7 +40,7 @@
* fix "extra fields not permitted" error when dataclass with `Extra.forbid` is validated multiple times, #4343 by @detachhead
* Add Python 3.9 and 3.10 examples to docs, #4339 by @Bobronium
* Discriminated union models now use `oneOf` instead of `anyOf` when generating OpenAPI schema definitions, #4335 by @MaxwellPayne
* Allow type checkers to infer inner type of `Json` type. `Json[list[str]]` will be now inferred as `list[str]`,
* Allow type checkers to infer inner type of `Json` type. `Json[list[str]]` will be now inferred as `list[str]`,
`Json[Any]` should be used instead of plain `Json`.
Runtime behaviour is not changed, #4332 by @Bobronium
* Allow empty string aliases by using a `alias is not None` check, rather than `bool(alias)`, #4253 by @sergeytsaplin
@@ -112,23 +112,23 @@ Pre-release, see [the GitHub release](https://github.com/pydantic/pydantic/relea
## v1.9.2 (2022-08-11)
**Revert Breaking Change**: _v1.9.1_ introduced a breaking change where model fields were
deep copied by default, this release reverts the default behaviour to match _v1.9.0_ and before,
deep copied by default, this release reverts the default behaviour to match _v1.9.0_ and before,
while also allow deep-copy behaviour via `copy_on_model_validation = 'deep'`. See #4092 for more information.
* Allow for shallow copies of model fields, `Config.copy_on_model_validation` is now a str which must be
`'none'`, `'deep'`, or `'shallow'` corresponding to not copying, deep copy & shallow copy; default `'shallow'`,
`'none'`, `'deep'`, or `'shallow'` corresponding to not copying, deep copy & shallow copy; default `'shallow'`,
#4093 by @timkpaine
## v1.9.1 (2022-05-19)
Thank you to pydantic's sponsors:
@tiangolo, @stellargraph, @JonasKs, @grillazz, @Mazyod, @kevinalh, @chdsbd, @povilasb, @povilasb, @jina-ai,
@mainframeindustries, @robusta-dev, @SendCloud, @rszamszur, @jodal, @hardbyte, @corleyma, @daddycocoaman,
@Rehket, @jokull, @reillysiemens, @westonsteimel, @primer-io, @koxudaxi, @browniebroke, @stradivari96,
@tiangolo, @stellargraph, @JonasKs, @grillazz, @Mazyod, @kevinalh, @chdsbd, @povilasb, @povilasb, @jina-ai,
@mainframeindustries, @robusta-dev, @SendCloud, @rszamszur, @jodal, @hardbyte, @corleyma, @daddycocoaman,
@Rehket, @jokull, @reillysiemens, @westonsteimel, @primer-io, @koxudaxi, @browniebroke, @stradivari96,
@adriangb, @kamalgill, @jqueguiner, @dev-zero, @datarootsio, @RedCarpetUp
for their kind support.
* Limit the size of `generics._generic_types_cache` and `generics._assigned_parameters`
* Limit the size of `generics._generic_types_cache` and `generics._assigned_parameters`
to avoid unlimited increase in memory usage, #4083 by @samuelcolvin
* Add Jupyverse and FPS as Jupyter projects using pydantic, #4082 by @davidbrochart
* Speedup `__isinstancecheck__` on pydantic models when the type is not a model, may also avoid memory "leaks", #4081 by @samuelcolvin
@@ -152,7 +152,7 @@ for their kind support.
Thank you to pydantic's sponsors:
@sthagen, @timdrijvers, @toinbis, @koxudaxi, @ginomempin, @primer-io, @and-semakin, @westonsteimel, @reillysiemens,
@es3n1n, @jokull, @JonasKs, @Rehket, @corleyma, @daddycocoaman, @hardbyte, @datarootsio, @jodal, @aminalaee, @rafsaf,
@es3n1n, @jokull, @JonasKs, @Rehket, @corleyma, @daddycocoaman, @hardbyte, @datarootsio, @jodal, @aminalaee, @rafsaf,
@jqueguiner, @chdsbd, @kevinalh, @Mazyod, @grillazz, @JonasKs, @simw, @leynier, @xfenix
for their kind support.
@@ -185,7 +185,7 @@ for their kind support.
### v1.9.0a1 (2021-12-18) Changes
* Add support for `Decimal`-specific validation configurations in `Field()`, additionally to using `condecimal()`,
* Add support for `Decimal`-specific validation configurations in `Field()`, additionally to using `condecimal()`,
to allow better support from editors and tooling, #3507 by @tiangolo
* Add `arm64` binaries suitable for MacOS with an M1 CPU to PyPI, #3498 by @samuelcolvin
* Fix issue where `None` was considered invalid when using a `Union` type containing `Any` or `object`, #3444 by @tharradine
@@ -193,7 +193,7 @@ for their kind support.
`pydantic.fields.ModelField`) to `__modify_schema__()` if present, #3434 by @jasujm
* Fix issue when pydantic fail to parse `typing.ClassVar` string type annotation, #3401 by @uriyyo
* Mention Python >= 3.9.2 as an alternative to `typing_extensions.TypedDict`, #3374 by @BvB93
* Changed the validator method name in the [Custom Errors example](https://pydantic-docs.helpmanual.io/usage/models/#custom-errors)
* Changed the validator method name in the [Custom Errors example](https://pydantic-docs.helpmanual.io/usage/models/#custom-errors)
to more accurately describe what the validator is doing; changed from `name_must_contain_space` to ` value_must_equal_bar`, #3327 by @michaelrios28
* Add `AmqpDsn` class, #3254 by @kludex
* Always use `Enum` value as default in generated JSON schema, #3190 by @joaommartins
@@ -211,19 +211,19 @@ for their kind support.
* Make multiple inheritance work when using `PrivateAttr`, #2989 by @hmvp
* Parse environment variables as JSON, if they have a `Union` type with a complex subfield, #2936 by @cbartz
* Prevent `StrictStr` permitting `Enum` values where the enum inherits from `str`, #2929 by @samuelcolvin
* Make `SecretsSettingsSource` parse values being assigned to fields of complex types when sourced from a secrets file,
* Make `SecretsSettingsSource` parse values being assigned to fields of complex types when sourced from a secrets file,
just as when sourced from environment variables, #2917 by @davidmreed
* add a dark mode to _pydantic_ documentation, #2913 by @gbdlin
* Make `pydantic-mypy` plugin compatible with `pyproject.toml` configuration, consistent with `mypy` changes.
* Make `pydantic-mypy` plugin compatible with `pyproject.toml` configuration, consistent with `mypy` changes.
See the [doc](https://pydantic-docs.helpmanual.io/mypy_plugin/#configuring-the-plugin) for more information, #2908 by @jrwalk
* add Python 3.10 support, #2885 by @PrettyWood
* Correctly parse generic models with `Json[T]`, #2860 by @geekingfrog
* Update contrib docs re: Python version to use for building docs, #2856 by @paxcodes
* Clarify documentation about _pydantic_'s support for custom validation and strict type checking,
* Clarify documentation about _pydantic_'s support for custom validation and strict type checking,
despite _pydantic_ being primarily a parsing library, #2855 by @paxcodes
* Fix schema generation for `Deque` fields, #2810 by @sergejkozin
* fix an edge case when mixing constraints and `Literal`, #2794 by @PrettyWood
* Fix postponed annotation resolution for `NamedTuple` and `TypedDict` when they're used directly as the type of fields
* Fix postponed annotation resolution for `NamedTuple` and `TypedDict` when they're used directly as the type of fields
within Pydantic models, #2760 by @jameysharp
* Fix bug when `mypy` plugin fails on `construct` method call for `BaseSettings` derived classes, #2753 by @uriyyo
* Add function overloading for a `pydantic.create_model` function, #2748 by @uriyyo
@@ -246,7 +246,7 @@ for their kind support.
and `postgresql+pygresql` schemes for `PostgresDsn`, #2567 by @postgres-asyncpg
* Enable the Hypothesis plugin to generate a constrained decimal when the `decimal_places` argument is specified, #2524 by @cwe5590
* Allow `collections.abc.Callable` to be used as type in Python 3.9, #2519 by @daviskirk
* Documentation update how to custom compile pydantic when using pip install, small change in `setup.py`
* Documentation update how to custom compile pydantic when using pip install, small change in `setup.py`
to allow for custom CFLAGS when compiling, #2517 by @peterroelants
* remove side effect of `default_factory` to run it only once even if `Config.validate_all` is set, #2515 by @PrettyWood
* Add lookahead to ip regexes for `AnyUrl` hosts. This allows urls with DNS labels
@@ -259,7 +259,7 @@ for their kind support.
* Add `PastDate` and `FutureDate` types, #2425 by @Kludex
* Support generating schema for `Generic` fields with subtypes, #2375 by @maximberg
* fix(encoder): serialize `NameEmail` to str, #2341 by @alecgerona
* add `Config.smart_union` to prevent coercion in `Union` if possible, see
* add `Config.smart_union` to prevent coercion in `Union` if possible, see
[the doc](https://pydantic-docs.helpmanual.io/usage/model_config/#smart-union) for more information, #2092 by @PrettyWood
* Add ability to use `typing.Counter` as a model field type, #2060 by @uriyyo
* Add parameterised subclasses to `__bases__` when constructing new parameterised classes, so that `A <: B => A[int] <: B[int]`, #2007 by @diabolo-dan
@@ -276,8 +276,8 @@ for their kind support.
A security vulnerability, level "moderate" is fixed in v1.8.2. Please upgrade **ASAP**.
See security advisory [CVE-2021-29510](https://github.com/pydantic/pydantic/security/advisories/GHSA-5jqp-qgf6-3pvh)
* **Security fix:** Fix `date` and `datetime` parsing so passing either `'infinity'` or `float('inf')`
(or their negative values) does not cause an infinite loop,
* **Security fix:** Fix `date` and `datetime` parsing so passing either `'infinity'` or `float('inf')`
(or their negative values) does not cause an infinite loop,
see security advisory [CVE-2021-29510](https://github.com/pydantic/pydantic/security/advisories/GHSA-5jqp-qgf6-3pvh)
* fix schema generation with Enum by generating a valid name, #2575 by @PrettyWood
* fix JSON schema generation with a `Literal` of an enum member, #2536 by @PrettyWood
@@ -289,7 +289,7 @@ for their kind support.
## v1.8.1 (2021-03-03)
Bug fixes for regressions and new features from `v1.8`
Bug fixes for regressions and new features from `v1.8`
* allow elements of `Config.field` to update elements of a `Field`, #2461 by @samuelcolvin
* fix validation with a `BaseModel` field and a custom root type, #2449 by @PrettyWood
@@ -303,8 +303,8 @@ Bug fixes for regressions and new features from `v1.8`
## v1.8 (2021-02-26)
Thank you to pydantic's sponsors:
@jorgecarleitao, @BCarley, @chdsbd, @tiangolo, @matin, @linusg, @kevinalh, @koxudaxi, @timdrijvers, @mkeen, @meadsteve,
@ginomempin, @primer-io, @and-semakin, @tomthorogood, @AjitZK, @westonsteimel, @Mazyod, @christippett, @CarlosDomingues,
@jorgecarleitao, @BCarley, @chdsbd, @tiangolo, @matin, @linusg, @kevinalh, @koxudaxi, @timdrijvers, @mkeen, @meadsteve,
@ginomempin, @primer-io, @and-semakin, @tomthorogood, @AjitZK, @westonsteimel, @Mazyod, @christippett, @CarlosDomingues,
@Kludex, @r-m-n
for their kind support.
@@ -395,7 +395,7 @@ for their kind support.
## v1.7.4 (2021-05-11)
* **Security fix:** Fix `date` and `datetime` parsing so passing either `'infinity'` or `float('inf')`
* **Security fix:** Fix `date` and `datetime` parsing so passing either `'infinity'` or `float('inf')`
(or their negative values) does not cause an infinite loop,
See security advisory [CVE-2021-29510](https://github.com/pydantic/pydantic/security/advisories/GHSA-5jqp-qgf6-3pvh)
@@ -434,7 +434,7 @@ for their kind support.
## v1.7 (2020-10-26)
Thank you to pydantic's sponsors:
@timdrijvers, @BCarley, @chdsbd, @tiangolo, @matin, @linusg, @kevinalh, @jorgecarleitao, @koxudaxi, @primer-api
@timdrijvers, @BCarley, @chdsbd, @tiangolo, @matin, @linusg, @kevinalh, @jorgecarleitao, @koxudaxi, @primer-api
for their kind support.
### Highlights
@@ -449,7 +449,7 @@ for their kind support.
* **Breaking Change:** remove `__field_defaults__`, add `default_factory` support with `BaseModel.construct`.
Use `.get_default()` method on fields in `__fields__` attribute instead, #1732 by @PrettyWood
* Rearrange CI to run linting as a separate job, split install recipes for different tasks, #2020 by @samuelcolvin
* Allows subclasses of generic models to make some, or all, of the superclass's type parameters concrete, while
* Allows subclasses of generic models to make some, or all, of the superclass's type parameters concrete, while
also defining new type parameters in the subclass, #2005 by @choogeboom
* Call validator with the correct `values` parameter type in `BaseModel.__setattr__`,
when `validate_assignment = True` in model config, #1999 by @me-ransh
@@ -470,17 +470,17 @@ for their kind support.
* add basic support of Pattern type in schema generation, #1767 by @PrettyWood
* Support custom title, description and default in schema of enums, #1748 by @PrettyWood
* Properly represent `Literal` Enums when `use_enum_values` is True, #1747 by @noelevans
* Allows timezone information to be added to strings to be formatted as time objects. Permitted formats are `Z` for UTC
* Allows timezone information to be added to strings to be formatted as time objects. Permitted formats are `Z` for UTC
or an offset for absolute positive or negative time shifts. Or the timezone data can be omitted, #1744 by @noelevans
* Add stub `__init__` with Python 3.6 signature for `ForwardRef`, #1738 by @sirtelemak
* Fix behaviour with forward refs and optional fields in nested models, #1736 by @PrettyWood
* add `Enum` and `IntEnum` as valid types for fields, #1735 by @PrettyWood
* Change default value of `__module__` argument of `create_model` from `None` to `'pydantic.main'`.
Set reference of created concrete model to it's module to allow pickling (not applied to models created in
* Change default value of `__module__` argument of `create_model` from `None` to `'pydantic.main'`.
Set reference of created concrete model to it's module to allow pickling (not applied to models created in
functions), #1686 by @Bobronium
* Add private attributes support, #1679 by @Bobronium
* add `config` to `@validate_arguments`, #1663 by @samuelcolvin
* Allow descendant Settings models to override env variable names for the fields defined in parent Settings models with
* Allow descendant Settings models to override env variable names for the fields defined in parent Settings models with
`env` in their `Config`. Previously only `env_prefix` configuration option was applicable, #1561 by @ojomio
* Support `ref_template` when creating schema `$ref`s, #1479 by @kilo59
* Add a `__call__` stub to `PyObject` so that mypy will know that it is callable, #1352 by @brianmaissy
@@ -490,7 +490,7 @@ for their kind support.
## v1.6.2 (2021-05-11)
* **Security fix:** Fix `date` and `datetime` parsing so passing either `'infinity'` or `float('inf')`
* **Security fix:** Fix `date` and `datetime` parsing so passing either `'infinity'` or `float('inf')`
(or their negative values) does not cause an infinite loop,
See security advisory [CVE-2021-29510](https://github.com/pydantic/pydantic/security/advisories/GHSA-5jqp-qgf6-3pvh)
@@ -559,7 +559,7 @@ Thank you to pydantic's sponsors: @matin, @tiangolo, @chdsbd, @jorgecarleitao, a
* Remove `typing_extensions` dependency for Python 3.8, #1342 by @prettywood
* Make `SecretStr` and `SecretBytes` initialization idempotent, #1330 by @atheuz
* document making secret types dumpable using the json method, #1328 by @atheuz
* Move all testing and build to github actions, add windows and macos binaries,
* Move all testing and build to github actions, add windows and macos binaries,
thank you @StephenBrown2 for much help, #1326 by @samuelcolvin
* fix card number length check in `PaymentCardNumber`, `PaymentCardBrand` now inherits from `str`, #1317 by @samuelcolvin
* Have `BaseModel` inherit from `Representation` to make mypy happy when overriding `__str__`, #1310 by @FuegoFro
+1 -1
View File
@@ -1,6 +1,6 @@
The MIT License (MIT)
Copyright (c) 2017, 2018, 2019, 2020, 2021 Samuel Colvin and other contributors
Copyright (c) 2017 - 2022 Samuel Colvin and other contributors
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
-6
View File
@@ -1,6 +0,0 @@
include LICENSE
include README.md
include HISTORY.md
graft tests
global-exclude __pycache__
global-exclude *.py[cod]
+10 -44
View File
@@ -1,56 +1,23 @@
.DEFAULT_GOAL := all
sources = pydantic tests docs/build
isort = isort $(sources)
black = black -S -l 120 --target-version py38 $(sources)
.PHONY: install-linting
install-linting:
pip install -r tests/requirements-linting.txt
pre-commit install
.PHONY: install-pydantic
install-pydantic:
python -m pip install -U wheel pip
pip install -r requirements.txt
SKIP_CYTHON=1 pip install -e .
.PHONY: install-testing
install-testing: install-pydantic
pip install -r tests/requirements-testing.txt
.PHONY: install-docs
install-docs: install-pydantic
pip install -U -r docs/requirements.txt
.PHONY: install
install: install-testing install-linting install-docs
@echo 'installed development requirements'
.PHONY: build-trace
build-trace:
python setup.py build_ext --force --inplace --define CYTHON_TRACE
.PHONY: build
build:
python setup.py build_ext --inplace
install:
python -m pip install -U pip
pip install -r requirements/all.txt
pip install -e .
.PHONY: format
format:
pyupgrade --py37-plus --exit-zero-even-if-changed `find $(sources) -name "*.py" -type f`
$(isort)
$(black)
pyupgrade --py37-plus --exit-zero-even-if-changed `find $(sources) -name "*.py" -type f`
isort $(sources)
black $(sources)
.PHONY: lint
lint:
flake8 $(sources)
$(isort) --check-only --df
$(black) --check --diff
.PHONY: check-dist
check-dist:
python setup.py check -ms
SKIP_CYTHON=1 python setup.py sdist
twine check dist/*
isort $(sources) --check-only --df
black $(sources) --check --diff
.PHONY: mypy
mypy:
@@ -66,7 +33,7 @@ pyright:
.PHONY: test
test:
pytest --cov=pydantic
coverage run -m pytest --durations=10
.PHONY: testcov
testcov: test
@@ -107,7 +74,6 @@ clean:
rm -rf build
rm -rf dist
rm -f pydantic/*.c pydantic/*.so
python setup.py clean
rm -rf site
rm -rf docs/_build
rm -rf docs/.changelog.md docs/.version.md docs/.tmp_schema_mappings.html
+13 -1
View File
@@ -8,7 +8,19 @@
[![versions](https://img.shields.io/pypi/pyversions/pydantic.svg)](https://github.com/pydantic/pydantic)
[![license](https://img.shields.io/github/license/pydantic/pydantic.svg)](https://github.com/pydantic/pydantic/blob/main/LICENSE)
Data validation and settings management using Python type hints.
Data validation using Python type hints.
---
## Notice
**This branch relates to development of pydantic V2 which is not yet ready for release.**
If you're a user of pydantic, you probably want either
[pydantic V1.10 Documentation](https://pydantic-docs.helpmanual.io/) or,
[`1.10.X-fixes` git branch](https://github.com/pydantic/pydantic/tree/1.10.X-fixes).
---
Fast and extensible, *pydantic* plays nicely with your linters/IDE/brain.
Define how data should be in pure, canonical Python 3.7+; validate it with *pydantic*.
+10 -10
View File
@@ -32,9 +32,9 @@ Here goes...
---
Enormous thanks to
[Eric Jolibois](https://github.com/PrettyWood), [Laurence Watson](https://github.com/Rabscuttler),
[Sebastián Ramírez](https://github.com/tiangolo), [Adrian Garcia Badaracco](https://github.com/adriangb),
[Tom Hamilton Stubber](https://github.com/tomhamiltonstubber), [Zac Hatfield-Dodds](https://github.com/Zac-HD),
[Eric Jolibois](https://github.com/PrettyWood), [Laurence Watson](https://github.com/Rabscuttler),
[Sebastián Ramírez](https://github.com/tiangolo), [Adrian Garcia Badaracco](https://github.com/adriangb),
[Tom Hamilton Stubber](https://github.com/tomhamiltonstubber), [Zac Hatfield-Dodds](https://github.com/Zac-HD),
[Tom](https://github.com/czotomo) & [Hasan Ramezani](https://github.com/hramezani)
for reviewing this blog post, putting up with (and correcting) my horrible typos and making great suggestions
that have made this post and Pydantic V2 materially better.
@@ -207,7 +207,7 @@ In future direct validation of JSON will also allow:
!!! note
Pydantic has always had special support for JSON, that is not going to change.
While in theory other formats could be specifically supported, the overheads and development time are
While in theory other formats could be specifically supported, the overheads and development time are
significant and I don't think there's another format that's
used widely enough to be worth specific logic. Other formats can be parsed to python then validated, similarly
when serialising, data can be exported to a python object, then serialised,
@@ -215,7 +215,7 @@ In future direct validation of JSON will also allow:
### Validation without a Model :thumbsup:
In pydantic V1 the core of all validation was a pydantic model, this led to a significant performance penalty
In pydantic V1 the core of all validation was a pydantic model, this led to a significant performance penalty
and extra complexity when the output data type was not a model.
pydantic-core operates on a tree of validators with no "model" type required at the base of that tree.
@@ -278,13 +278,13 @@ class MyModel(BaseModel):
@validator('timestamp', mode='wrap')
def validate_timestamp(cls, v, handler):
if v == 'now':
# we don't want to bother with further validation,
# we don't want to bother with further validation,
# just return the new value
return datetime.now()
try:
return handler(v)
except ValidationError:
# validation failed, in this case we want to
# validation failed, in this case we want to
# return a default value
return datetime(2000, 1, 1)
```
@@ -366,7 +366,7 @@ from pydantic import BaseModel, EmailStr, validator
class User(BaseModel):
email: EmailStr
home_country: str
@validator('home_country')
def check_home_country(cls, v, context):
if v not in context['countries']:
@@ -735,7 +735,7 @@ The emoji here is just for variation, I'm not frowning about any of this, these
1. `__root__` custom root models are no longer necessary since validation on any supported data type is allowed
without a model
2. `.parse_file()` and `.parse_raw()`, partially replaced with `.model_validate_json()`,
2. `.parse_file()` and `.parse_raw()`, partially replaced with `.model_validate_json()`,
see [model methods](#model-namespace-cleanup)
3. `.schema_json()` & `.copy()`, see [model methods](#model-namespace-cleanup)
4. `TypeError` are no longer considered as validation errors, but rather as internal errors, this is to better
@@ -759,7 +759,7 @@ The emoji here is just for variation, I'm not frowning about any of this, these
* `json_encoders` - see the export "mode" discussion [above](#improvements-to-dumpingserializationexport)
* `underscore_attrs_are_private` we should just choose a sensible default
* `smart_union` - all unions are now "smart"
9. `dict(model)` functionality should be removed, there's a much clearer distinction now that in 2017 when I
9. `dict(model)` functionality should be removed, there's a much clearer distinction now that in 2017 when I
implemented this between a model and a dict
## Features Remaining :neutral_face:
+1 -1
View File
@@ -13,7 +13,7 @@ and [`st.from_type()`](https://hypothesis.readthedocs.io/en/latest/data.html#hyp
strategies support them without any user configuration.
!!! warning
Please note, while the plugin supports these types, hypothesis will(currently) generate values outside
Please note, while the plugin supports these types, hypothesis will(currently) generate values outside
of given args for the constrained function types.
+15 -15
View File
@@ -102,9 +102,9 @@ If validation fails pydantic will raise an error with a breakdown of what was wr
So *pydantic* uses some cool new language features, but why should I actually go and use it?
**plays nicely with your IDE/linter/brain**
: There's no new schema definition micro-language to learn. If you know how to use Python type hints,
you know how to use *pydantic*. Data structures are just instances of classes you define with type annotations,
so auto-completion, linting, [mypy](usage/mypy.md), IDEs (especially [PyCharm](pycharm_plugin.md)),
: There's no new schema definition micro-language to learn. If you know how to use Python type hints,
you know how to use *pydantic*. Data structures are just instances of classes you define with type annotations,
so auto-completion, linting, [mypy](usage/mypy.md), IDEs (especially [PyCharm](pycharm_plugin.md)),
and your intuition should all work properly with your validated data.
**dual use**
@@ -117,15 +117,15 @@ So *pydantic* uses some cool new language features, but why should I actually go
it's generally as fast or faster than most similar libraries.
**validate complex structures**
: use of [recursive *pydantic* models](usage/models.md#recursive-models), `typing`'s
[standard types](usage/types.md#standard-library-types) (e.g. `List`, `Tuple`, `Dict` etc.) and
: use of [recursive *pydantic* models](usage/models.md#recursive-models), `typing`'s
[standard types](usage/types.md#standard-library-types) (e.g. `List`, `Tuple`, `Dict` etc.) and
[validators](usage/validators.md) allow
complex data schemas to be clearly and easily defined, validated, and parsed.
**extensible**
: *pydantic* allows [custom data types](usage/types.md#custom-data-types) to be defined or you can extend validation
: *pydantic* allows [custom data types](usage/types.md#custom-data-types) to be defined or you can extend validation
with methods on a model decorated with the [`validator`](usage/validators.md) decorator.
**dataclasses integration**
: As well as `BaseModel`, *pydantic* provides
a [`dataclass`](usage/dataclasses.md) decorator which creates (almost) vanilla Python dataclasses with input
@@ -140,14 +140,14 @@ Hundreds of organisations and packages are using *pydantic*, including:
fast to code and ready for production, based on *pydantic* and Starlette.
[Project Jupyter](https://jupyter.org/)
: developers of the Jupyter notebook are using *pydantic*
: developers of the Jupyter notebook are using *pydantic*
[for subprojects](https://github.com/pydantic/pydantic/issues/773), through the FastAPI-based Jupyter server
[Jupyverse](https://github.com/jupyter-server/jupyverse), and for [FPS](https://github.com/jupyter-server/fps)'s
configuration management.
**Microsoft**
: are using *pydantic* (via FastAPI) for
[numerous services](https://github.com/tiangolo/fastapi/pull/26#issuecomment-463768795), some of which are
: are using *pydantic* (via FastAPI) for
[numerous services](https://github.com/tiangolo/fastapi/pull/26#issuecomment-463768795), some of which are
"getting integrated into the core Windows product and some Office products."
**Amazon Web Services**
@@ -179,7 +179,7 @@ Hundreds of organisations and packages are using *pydantic*, including:
[tools to debug and profile Python applications on Kubernetes](https://home.robusta.dev/python/) use
*pydantic* models.
For a more comprehensive list of open-source projects using *pydantic* see the
For a more comprehensive list of open-source projects using *pydantic* see the
[list of dependents on github](https://github.com/pydantic/pydantic/network/dependents).
## Discussion of Pydantic
@@ -190,14 +190,14 @@ Podcasts and videos discussing pydantic.
: Michael Kennedy and Samuel Colvin, the creator of *pydantic*, dive into the history of pydantic and its many uses and benefits.
[Podcast.\_\_init\_\_](https://www.pythonpodcast.com/pydantic-data-validation-episode-263/){target=_blank}
: Discussion about where *pydantic* came from and ideas for where it might go next with
: Discussion about where *pydantic* came from and ideas for where it might go next with
Samuel Colvin the creator of pydantic.
[Python Bytes Podcast](https://pythonbytes.fm/episodes/show/157/oh-hai-pandas-hold-my-hand){target=_blank}
: "*This is a sweet simple framework that solves some really nice problems... Data validations and settings management
using Python type annotations, and it's the Python type annotations that makes me really extra happy... It works
: "*This is a sweet simple framework that solves some really nice problems... Data validations and settings management
using Python type annotations, and it's the Python type annotations that makes me really extra happy... It works
automatically with all the IDE's you already have.*" --Michael Kennedy
[Python pydantic Introduction Give your data classes super powers](https://www.youtube.com/watch?v=WJmqgJn9TXg){target=_blank}
: a talk by Alexander Hultnér originally for the Python Pizza Conference introducing new users to pydantic and walking
: a talk by Alexander Hultnér originally for the Python Pizza Conference introducing new users to pydantic and walking
through the core features of pydantic.
+1 -1
View File
@@ -17,7 +17,7 @@ conda install pydantic -c conda-forge
## Compiled with Cython
*pydantic* can optionally be compiled with [cython](https://cython.org/) which should give a 30-50% performance improvement.
*pydantic* can optionally be compiled with [cython](https://cython.org/) which should give a 30-50% performance improvement.
By default `pip install` provides optimized binaries via [PyPI](https://pypi.org/project/pydantic/#files) for Linux, MacOS and 64bit Windows.
+1 -1
View File
@@ -90,7 +90,7 @@ To get started, all you need to do is create a `mypy.ini` file with following co
plugins = pydantic.mypy
```
The plugin is compatible with mypy versions `>=0.910`.
The plugin is compatible with mypy versions `>=0.930`.
See the [mypy usage](usage/mypy.md) and [plugin configuration](#configuring-the-plugin) docs for more details.
+1 -1
View File
@@ -1,4 +1,4 @@
While pydantic will work well with any IDE out of the box, a
While pydantic will work well with any IDE out of the box, a
[PyCharm plugin](https://plugins.jetbrains.com/plugin/12861-pydantic)
offering improved pydantic integration is available on the JetBrains Plugins Repository for PyCharm.
You can install the plugin for free from the plugin marketplace
-15
View File
@@ -1,15 +0,0 @@
autoflake==1.5.3
ansi2html==1.8.0
flake8==5.0.4
flake8-quotes==3.3.1
hypothesis==6.54.4
markdown-include==0.7.0
mdx-truly-sane-lists==1.3
mkdocs==1.3.1
mkdocs-exclude==1.0.2
mkdocs-material==8.4.2
pyupgrade==2.37.3
sqlalchemy
orjson
ujson
+2 -2
View File
@@ -1,9 +1,9 @@
!!! note
**Admission:** I (the primary developer of *pydantic*) also develop python-devtools.
[python-devtools](https://python-devtools.helpmanual.io/) (`pip install devtools`) provides a number of tools which
are useful during Python development, including `debug()` an alternative to `print()` which formats output in a way
which should be easier to read than `print` as well as giving information about which file/line the print statement
which should be easier to read than `print` as well as giving information about which file/line the print statement
is on and what value was printed.
*pydantic* integrates with *devtools* by implementing the `__pretty__` method on most public classes.
+2 -2
View File
@@ -112,12 +112,12 @@ not be included in the model schemas. **Note**: this means that attributes on th
: whether to treat any underscore non-class var attrs as private, or leave them as is; see [Private model attributes](models.md#private-model-attributes)
**`copy_on_model_validation`**
: string literal to control how models instances are processed during validation,
: string literal to control how models instances are processed during validation,
with the following means (see [#4093](https://github.com/pydantic/pydantic/pull/4093) for a full discussion of the changes to this field):
* `'none'` - models are not copied on validation, they're simply kept "untouched"
* `'shallow'` - models are shallow copied, this is the default
* `'deep'` - models are deep copied
* `'deep'` - models are deep copied
**`smart_union`**
: whether _pydantic_ should try to check all types inside `Union` to prevent undesired coercion; see [the dedicated section](#smart-union)
+30 -30
View File
@@ -1,4 +1,4 @@
The primary means of defining objects in *pydantic* is via models
The primary means of defining objects in *pydantic* is via models
(models are simply classes which inherit from `BaseModel`).
You can think of models as similar to types in strictly typed languages, or as the requirements of a single endpoint
@@ -27,9 +27,9 @@ class User(BaseModel):
id: int
name = 'Jane Doe'
```
`User` here is a model with two fields `id` which is an integer and is required,
`User` here is a model with two fields `id` which is an integer and is required,
and `name` which is a string and is not required (it has a default value). The type of `name` is inferred from the
default value, and so a type annotation is not required (however note [this](#field-ordering) warning about field
default value, and so a type annotation is not required (however note [this](#field-ordering) warning about field
order when some fields do not have type annotations).
```py
user = User(id='123')
@@ -65,15 +65,15 @@ This model is mutable so field values can be changed.
### Model properties
The example above only shows the tip of the iceberg of what models can do.
The example above only shows the tip of the iceberg of what models can do.
Models possess the following methods and attributes:
`dict()`
: returns a dictionary of the model's fields and values;
: returns a dictionary of the model's fields and values;
cf. [exporting models](exporting_models.md#modeldict)
`json()`
: returns a JSON string representation `dict()`;
: returns a JSON string representation `dict()`;
cf. [exporting models](exporting_models.md#modeljson)
`copy()`
@@ -99,7 +99,7 @@ Models possess the following methods and attributes:
: returns a JSON string representation of `schema()`; cf. [schema](schema.md)
`construct()`
: a class method for creating models without running validation;
: a class method for creating models without running validation;
cf. [Creating models without validation](#creating-models-without-validation)
`__fields_set__`
@@ -159,7 +159,7 @@ Arbitrary classes are processed by *pydantic* using the `GetterDict` class (see
provide a dictionary-like interface to any class. You can customise how this works by setting your own
sub-class of `GetterDict` as the value of `Config.getter_dict` (see [config](model_config.md)).
You can also customise class validation using [root_validators](validators.md#root-validators) with `pre=True`.
You can also customise class validation using [root_validators](validators.md#root-validators) with `pre=True`.
In this case your validator function will be passed a `GetterDict` instance which you may copy and modify.
The `GetterDict` instance will be called for each field with a sentinel as a fallback (if no other default
@@ -240,12 +240,12 @@ You can also define your own error classes, which can specify a custom error cod
!!! warning
To quote the [official `pickle` docs](https://docs.python.org/3/library/pickle.html),
"The pickle module is not secure against erroneous or maliciously constructed data.
Never unpickle data received from an untrusted or unauthenticated source."
Never unpickle data received from an untrusted or unauthenticated source."
!!! info
Because it can result in arbitrary code execution, as a security measure, you need
to explicitly pass `allow_pickle` to the parsing function in order to load `pickle` data.
### Creating models without validation
*pydantic* also provides the `construct()` method which allows models to be created **without validation** this
@@ -258,11 +258,11 @@ as efficiently as possible (`construct()` is generally around 30x faster than cr
{!.tmp_examples/models_construct.md!}
The `_fields_set` keyword argument to `construct()` is optional, but allows you to be more precise about
The `_fields_set` keyword argument to `construct()` is optional, but allows you to be more precise about
which fields were originally set and which weren't. If it's omitted `__fields_set__` will just be the keys
of the data provided.
of the data provided.
For example, in the example above, if `_fields_set` was not provided,
For example, in the example above, if `_fields_set` was not provided,
`new_user.__fields_set__` would be `{'id', 'age', 'name'}`.
## Generic Models
@@ -292,12 +292,12 @@ you would expect mypy to provide if you were to declare the type without using `
Internally, pydantic uses `create_model` to generate a (cached) concrete `BaseModel` at runtime,
so there is essentially zero overhead introduced by making use of `GenericModel`.
To inherit from a GenericModel without replacing the `TypeVar` instance, a class must also inherit from
To inherit from a GenericModel without replacing the `TypeVar` instance, a class must also inherit from
`typing.Generic`:
{!.tmp_examples/models_generics_inheritance.md!}
You can also create a generic subclass of a `GenericModel` that partially or fully replaces the type
You can also create a generic subclass of a `GenericModel` that partially or fully replaces the type
parameters in the superclass.
{!.tmp_examples/models_generics_inheritance_extend.md!}
@@ -311,7 +311,7 @@ Using the same TypeVar in nested models allows you to enforce typing relationshi
{!.tmp_examples/models_generics_nested.md!}
Pydantic also treats `GenericModel` similarly to how it treats built-in generic types like `List` and `Dict` when it
comes to leaving them unparameterized, or using bounded `TypeVar` instances:
comes to leaving them unparameterized, or using bounded `TypeVar` instances:
* If you don't specify parameters before instantiating the generic model, they will be treated as `Any`
* You can parametrize models with one or more *bounded* parameters to add subclass checks
@@ -331,7 +331,7 @@ Here `StaticFoobarModel` and `DynamicFoobarModel` are identical.
!!! warning
See the note in [Required Optional Fields](#required-optional-fields) for the distinction between an ellipsis as a
field default and annotation-only fields.
field default and annotation-only fields.
See [pydantic/pydantic#1047](https://github.com/pydantic/pydantic/issues/1047) for more details.
Fields are defined by either a tuple of the form `(<type>, <default value>)` or just a default value. The
@@ -356,7 +356,7 @@ Those methods have the exact same keyword arguments as `create_model`.
## Custom Root Types
Pydantic models can be defined with a custom root type by declaring the `__root__` field.
Pydantic models can be defined with a custom root type by declaring the `__root__` field.
The root type can be any type supported by pydantic, and is specified by the type hint on the `__root__` field.
The root value can be passed to the model `__init__` via the `__root__` keyword argument, or as
@@ -371,7 +371,7 @@ the following logic is used:
the argument itself is always validated against the custom root type.
* For other custom root types, if the dict has precisely one key with the value `__root__`,
the corresponding value will be validated against the custom root type.
* Otherwise, the dict itself is validated against the custom root type.
* Otherwise, the dict itself is validated against the custom root type.
This is demonstrated in the following example:
@@ -380,7 +380,7 @@ This is demonstrated in the following example:
!!! warning
Calling the `parse_obj` method on a dict with the single key `"__root__"` for non-mapping custom root types
is currently supported for backwards compatibility, but is not recommended and may be dropped in a future version.
If you want to access items in the `__root__` field directly or to iterate over the items, you can implement custom `__iter__` and `__getitem__` functions, as shown in the following example.
{!.tmp_examples/models_custom_root_access.md!}
@@ -410,7 +410,7 @@ Pydantic models can be used alongside Python's
Field order is important in models for the following reasons:
* validation is performed in the order fields are defined; [fields validators](validators.md)
* validation is performed in the order fields are defined; [fields validators](validators.md)
can access the values of earlier fields, but not later ones
* field order is preserved in the model [schema](schema.md)
* field order is preserved in [validation errors](#error-handling)
@@ -430,7 +430,7 @@ all fields without an annotation. Within their respective groups, fields remain
## Required fields
To declare a field as required, you may declare it using just an annotation, or you may use an ellipsis (`...`)
To declare a field as required, you may declare it using just an annotation, or you may use an ellipsis (`...`)
as the value:
{!.tmp_examples/models_required_fields.md!}
@@ -488,7 +488,7 @@ using `PrivateAttr`:
{!.tmp_examples/private_attributes.md!}
Private attribute names must start with underscore to prevent conflicts with model fields: both `_attr` and `__attr__`
Private attribute names must start with underscore to prevent conflicts with model fields: both `_attr` and `__attr__`
are supported.
If `Config.underscore_attrs_are_private` is `True`, any non-ClassVar underscore attribute will be treated as private:
@@ -503,7 +503,7 @@ logic used to populate pydantic models in a more ad-hoc way. This function behav
`BaseModel.parse_obj`, but works with arbitrary pydantic-compatible types.
This is especially useful when you want to parse results into a type that is not a direct subclass of `BaseModel`.
For example:
For example:
{!.tmp_examples/parse_obj_as.md!}
@@ -520,7 +520,7 @@ For example:
{!.tmp_examples/models_data_conversion.md!}
This is a deliberate decision of *pydantic*, and in general it's the most useful approach. See
This is a deliberate decision of *pydantic*, and in general it's the most useful approach. See
[here](https://github.com/pydantic/pydantic/issues/578) for a longer discussion on the subject.
Nevertheless, [strict type checking](types.md#strict-types) is partially supported.
@@ -537,14 +537,14 @@ The generated signature will also respect custom `__init__` functions:
{!.tmp_examples/models_signature_custom_init.md!}
To be included in the signature, a field's alias or name must be a valid Python identifier.
*pydantic* prefers aliases over names, but may use field names if the alias is not a valid Python identifier.
To be included in the signature, a field's alias or name must be a valid Python identifier.
*pydantic* prefers aliases over names, but may use field names if the alias is not a valid Python identifier.
If a field's alias and name are both invalid identifiers, a `**data` argument will be added.
In addition, the `**data` argument will always be present in the signature if `Config.extra` is `Extra.allow`.
!!! note
Types in the model signature are the same as declared in model annotations,
Types in the model signature are the same as declared in model annotations,
not necessarily all the types that can actually be provided to that field.
This may be fixed one day once [#1055](https://github.com/pydantic/pydantic/issues/1055) is solved.
@@ -555,5 +555,5 @@ In addition, the `**data` argument will always be present in the signature if `C
{!.tmp_examples/models_structural_pattern_matching.md!}
!!! note
A match-case statement may seem as if it creates a new model, but don't be fooled;
A match-case statement may seem as if it creates a new model, but don't be fooled;
it is just syntactic sugar for getting an attribute and either comparing it or declaring and initializing it.
+2 -1
View File
@@ -619,7 +619,7 @@ For URI/URL validation the following types are available:
!!! warning
In V1.10.0 and v1.10.1 `stricturl` also took an optional `quote_plus` argument and URL components were percent
encoded in some cases. This feature was removed in v1.10.2, see
encoded in some cases. This feature was removed in v1.10.2, see
[#4470](https://github.com/pydantic/pydantic/pull/4470) for explanation and more details.
The above types (which all inherit from `AnyUrl`) will attempt to give descriptive errors when invalid URLs are
@@ -683,6 +683,7 @@ If further validation is required, these properties can be used by validators to
Also, Chrome, Firefox, and Safari all currently accept `http://exam_ple.com` as a URL, so we're in good
(or at least big) company.
### Color Type
You can use the `Color` data type for storing colors as per
+4 -4
View File
@@ -251,15 +251,15 @@ The specific configuration **`frozen`** (in beta) has a special meaning.
It prevents other code from changing a model instance once it's created, keeping it **"frozen"**.
When using the second version to declare `frozen=True` (with **keyword arguments** in the class definition),
Pylance can use it to help you check in your code and **detect errors** when something is trying to set values
When using the second version to declare `frozen=True` (with **keyword arguments** in the class definition),
Pylance can use it to help you check in your code and **detect errors** when something is trying to set values
in a model that is "frozen".
![VS Code strict type errors with model](./img/vs_code_08.png)
## BaseSettings and ignoring Pylance/pyright errors
Pylance/pyright does not work well with [`BaseSettings`](./usage/settings.md) - fields in settings classes can be
Pylance/pyright does not work well with [`BaseSettings`](./usage/settings.md) - fields in settings classes can be
configured via environment variables and therefore "required" fields do not have to be explicitly set when
initialising a settings instance. However, pyright considers these fields as "required" and will therefore
show an error when they're not set.
@@ -284,7 +284,7 @@ class Knight(BaseModel):
title: str = Field(default='Sir Lancelot') # this is okay
age: int = Field(23) # this works fine at runtime but will case an error for pyright
lance = Knight() # error: Argument missing for parameter "age"
lance = Knight() # error: Argument missing for parameter "age"
```
Like the issue with `BaseSettings`, this is a limitation of dataclass transforms and cannot be fixed in pydantic.
+1 -1
View File
@@ -81,7 +81,7 @@ markdown_extensions:
emoji_index: !!python/name:materialx.emoji.twemoji
emoji_generator: !!python/name:materialx.emoji.to_svg
- pymdownx.tabbed:
alternate_style: true
alternate_style: true
plugins:
- search
+1 -2
View File
@@ -13,7 +13,7 @@ from .networks import *
from .parse import Protocol
from .tools import *
from .types import *
from .version import VERSION, compiled
from .version import VERSION
__version__ = VERSION
@@ -126,6 +126,5 @@ __all__ = [
'PastDate',
'FutureDate',
# version
'compiled',
'VERSION',
]
+29 -41
View File
@@ -2,11 +2,10 @@ import json
from enum import Enum
from typing import TYPE_CHECKING, Any, Callable, Dict, ForwardRef, Optional, Tuple, Type, Union
from typing_extensions import Literal, Protocol
from typing_extensions import Literal, Protocol, TypedDict
from .typing import AnyArgTCallable, AnyCallable
from .utils import GetterDict
from .version import compiled
if TYPE_CHECKING:
from typing import overload
@@ -37,45 +36,34 @@ class Extra(str, Enum):
forbid = 'forbid'
# https://github.com/cython/cython/issues/4003
# Will be fixed with Cython 3 but still in alpha right now
if not compiled:
from typing_extensions import TypedDict
class ConfigDict(TypedDict, total=False):
title: Optional[str]
anystr_lower: bool
anystr_strip_whitespace: bool
min_anystr_length: int
max_anystr_length: Optional[int]
validate_all: bool
extra: Extra
allow_mutation: bool
frozen: bool
allow_population_by_field_name: bool
use_enum_values: bool
fields: Dict[str, Union[str, Dict[str, str]]]
validate_assignment: bool
error_msg_templates: Dict[str, str]
arbitrary_types_allowed: bool
orm_mode: bool
getter_dict: Type[GetterDict]
alias_generator: Optional[Callable[[str], str]]
keep_untouched: Tuple[type, ...]
schema_extra: Union[Dict[str, object], 'SchemaExtraCallable']
json_loads: Callable[[str], object]
json_dumps: AnyArgTCallable[str]
json_encoders: Dict[Type[object], AnyCallable]
underscore_attrs_are_private: bool
allow_inf_nan: bool
# whether or not inherited models as fields should be reconstructed as base model
copy_on_model_validation: bool
# whether dataclass `__post_init__` should be run after validation
post_init_call: Literal['before_validation', 'after_validation']
else:
ConfigDict = dict # type: ignore
class ConfigDict(TypedDict, total=False):
title: Optional[str]
anystr_lower: bool
anystr_strip_whitespace: bool
min_anystr_length: int
max_anystr_length: Optional[int]
validate_all: bool
extra: Extra
allow_mutation: bool
frozen: bool
allow_population_by_field_name: bool
use_enum_values: bool
fields: Dict[str, Union[str, Dict[str, str]]]
validate_assignment: bool
error_msg_templates: Dict[str, str]
arbitrary_types_allowed: bool
orm_mode: bool
getter_dict: Type[GetterDict]
alias_generator: Optional[Callable[[str], str]]
keep_untouched: Tuple[type, ...]
schema_extra: Union[Dict[str, object], 'SchemaExtraCallable']
json_loads: Callable[[str], object]
json_dumps: AnyArgTCallable[str]
json_encoders: Dict[Type[object], AnyCallable]
underscore_attrs_are_private: bool
allow_inf_nan: bool
copy_on_model_validation: Literal['none', 'deep', 'shallow']
post_init_call: Literal['before_validation', 'after_validation']
class BaseConfig:
+2 -2
View File
@@ -204,7 +204,7 @@ def dataclass(
else:
dc_cls_doc = cls.__doc__ or '' # needs to be done before generating dataclass
if sys.version_info >= (3, 10):
dc_cls = dataclasses.dataclass(
dc_cls = dataclasses.dataclass( # type: ignore[call-overload]
cls,
init=init,
repr=repr,
@@ -215,7 +215,7 @@ def dataclass(
kw_only=kw_only,
)
else:
dc_cls = dataclasses.dataclass( # type: ignore
dc_cls = dataclasses.dataclass(
cls, init=init, repr=repr, eq=eq, order=order, unsafe_hash=unsafe_hash, frozen=frozen
)
default_validate_on_init = True
+11 -26
View File
@@ -68,7 +68,7 @@ from pydantic.utils import is_valid_field
try:
from mypy.types import TypeVarDef # type: ignore[attr-defined]
except ImportError: # pragma: no cover
# Backward-compatible with TypeVarDef from Mypy 0.910.
# Backward-compatible with TypeVarDef from Mypy 0.930.
from mypy.types import TypeVarType as TypeVarDef
CONFIGFILE_KEY = 'pydantic-mypy'
@@ -164,11 +164,7 @@ class PydanticPlugin(Plugin):
# Functions which use `ParamSpec` can be overloaded, exposing the callable's types as a parameter
# Pydantic calls the default factory without any argument, so we retrieve the first item
if isinstance(default_factory_type, Overloaded):
if MYPY_VERSION_TUPLE > (0, 910):
default_factory_type = default_factory_type.items[0]
else:
# Mypy0.910 exposes the items of overloaded types in a function
default_factory_type = default_factory_type.items()[0] # type: ignore[operator]
default_factory_type = default_factory_type.items[0]
if isinstance(default_factory_type, CallableType):
ret_type = default_factory_type.ret_type
@@ -448,23 +444,18 @@ class PydanticModelTransformer:
obj_type = ctx.api.named_type(f'{BUILTINS_NAME}.object')
self_tvar_name = '_PydanticBaseModel' # Make sure it does not conflict with other names in the class
tvar_fullname = ctx.cls.fullname + '.' + self_tvar_name
tvd = TypeVarDef(self_tvar_name, tvar_fullname, -1, [], obj_type)
# requires mypy>0.910
self_type = TypeVarDef(self_tvar_name, tvar_fullname, -1, [], obj_type)
self_tvar_expr = TypeVarExpr(self_tvar_name, tvar_fullname, [], obj_type)
ctx.cls.info.names[self_tvar_name] = SymbolTableNode(MDEF, self_tvar_expr)
# Backward-compatible with TypeVarDef from Mypy 0.910.
if isinstance(tvd, TypeVarType):
self_type = tvd
else:
self_type = TypeVarType(tvd) # type: ignore[call-arg]
add_method(
ctx,
'construct',
construct_arguments,
return_type=self_type,
self_type=self_type,
tvar_def=tvd,
tvar_def=self_type,
is_classmethod=True,
)
@@ -829,22 +820,16 @@ def parse_toml(config_file: str) -> Optional[Dict[str, Any]]:
if not config_file.endswith('.toml'):
return None
read_mode = 'rb'
if sys.version_info >= (3, 11):
import tomllib as toml_
else:
try:
import tomli as toml_
except ImportError:
# older versions of mypy have toml as a dependency, not tomli
read_mode = 'r'
try:
import toml as toml_ # type: ignore[no-redef]
except ImportError: # pragma: no cover
import warnings
except ImportError: # pragma: no cover
import warnings
warnings.warn('No TOML parser installed, cannot read configuration from `pyproject.toml`.')
return None
warnings.warn('No TOML parser installed, cannot read configuration from `pyproject.toml`.')
return None
with open(config_file, read_mode) as rf:
return toml_.load(rf) # type: ignore[arg-type]
with open(config_file, 'rb') as rf:
return toml_.load(rf)
+3 -3
View File
@@ -46,10 +46,10 @@ except ImportError:
TypingGenericAlias = ()
try:
from types import UnionType as TypesUnionType # type: ignore
from types import UnionType as TypesUnionType
except ImportError:
# python < 3.10 does not have UnionType (str | int, byte | bool and so on)
TypesUnionType = ()
TypesUnionType = () # type: ignore[misc,assignment]
if sys.version_info < (3, 9):
@@ -243,7 +243,7 @@ else:
def is_union(tp: Optional[Type[Any]]) -> bool:
return tp is Union or tp is types.UnionType # noqa: E721
WithArgsTypes = (typing._GenericAlias, types.GenericAlias, types.UnionType)
WithArgsTypes = (typing._GenericAlias, types.GenericAlias, types.UnionType) # type: ignore[attr-defined]
if sys.version_info < (3, 9):
-18
View File
@@ -1,5 +1,4 @@
import keyword
import warnings
import weakref
from collections import OrderedDict, defaultdict, deque
from copy import deepcopy
@@ -139,23 +138,6 @@ def import_string(dotted_path: str) -> Any:
raise ImportError(f'Module "{module_path}" does not define a "{class_name}" attribute') from e
def truncate(v: Union[str], *, max_len: int = 80) -> str:
"""
Truncate a value and add a unicode ellipsis (three dots) to the end if it was too long
"""
warnings.warn('`truncate` is no-longer used by pydantic and is deprecated', DeprecationWarning)
if isinstance(v, str) and len(v) > (max_len - 2):
# -3 so quote + string + … + quote has correct length
return (v[: (max_len - 3)] + '').__repr__()
try:
v = v.__repr__()
except TypeError:
v = v.__class__.__repr__(v) # in case v is a type
if len(v) > max_len:
v = v[: max_len - 1] + ''
return v
def sequence_like(v: Any) -> bool:
return isinstance(v, (list, tuple, set, frozenset, GeneratorType, deque))
+2 -13
View File
@@ -1,16 +1,6 @@
__all__ = 'compiled', 'VERSION', 'version_info'
__all__ = 'VERSION', 'version_info'
VERSION = '1.10.2'
try:
import cython # type: ignore
except ImportError:
compiled: bool = False
else: # pragma: no cover
try:
compiled = cython.compiled
except AttributeError:
compiled = False
VERSION = '2.0.0.dev0'
def version_info() -> str:
@@ -29,7 +19,6 @@ def version_info() -> str:
info = {
'pydantic version': VERSION,
'pydantic compiled': compiled,
'install path': Path(__file__).resolve().parent,
'python version': sys.version,
'platform': platform.platform(),
+143
View File
@@ -0,0 +1,143 @@
[build-system]
requires = ['hatchling']
build-backend = 'hatchling.build'
[tool.hatch.version]
path = 'pydantic/version.py'
[project]
name = 'pydantic'
description = 'Data validation using Python type hints'
authors = [
{name = 'Samuel Colvin', email = 's@muelcolvin.com'},
{name = 'Eric Jolibois', email = 'em.jolibois@gmail.com'},
{name = 'Hasan Ramezani', email = 'hasan.r67@gmail.com'},
]
license = {file = 'LICENSE'}
readme = 'README.md'
classifiers = [
'Development Status :: 5 - Production/Stable',
'Programming Language :: Python',
'Programming Language :: Python :: 3',
'Programming Language :: Python :: 3 :: Only',
'Programming Language :: Python :: 3.7',
'Programming Language :: Python :: 3.8',
'Programming Language :: Python :: 3.9',
'Programming Language :: Python :: 3.10',
'Programming Language :: Python :: 3.11',
'Intended Audience :: Developers',
'Intended Audience :: Information Technology',
'Intended Audience :: System Administrators',
'License :: OSI Approved :: MIT License',
'Operating System :: Unix',
'Operating System :: POSIX :: Linux',
'Environment :: Console',
'Environment :: MacOS X',
'Framework :: Hypothesis',
'Topic :: Software Development :: Libraries :: Python Modules',
'Topic :: Internet',
]
requires-python = '>=3.7'
dependencies = [ 'typing-extensions>=4.1.0' ]
optional-dependencies = { email = ['email-validator>=1.0.3'], dotenv = ['python-dotenv>=0.10.4'] }
dynamic = ['version']
entry-points.hypothesis = {_ = 'pydantic._hypothesis_plugin'}
[project.urls]
Homepage = 'https://github.com/pydantic/pydantic'
Documentation = 'https://pydantic-docs.helpmanual.io'
Funding = 'https://github.com/sponsors/samuelcolvin'
Source = 'https://github.com/pydantic/pydantic'
Changelog = 'https://pydantic-docs.helpmanual.io/changelog'
[tool.pytest.ini_options]
testpaths = 'tests'
filterwarnings = [
'error',
'ignore:path is deprecated.*:DeprecationWarning:certifi',
]
[tool.flake8]
max_line_length = 120
max_complexity = 14
inline_quotes = 'single'
multiline_quotes = 'double'
ignore = ['E203', 'W503']
per_file_ignores = [
'docs/examples/schema_unenforced_constraints.py:F811',
'docs/examples/validation_decorator_async.py:E402',
'docs/examples/types_constrained.py:F722',
]
[tool.coverage.run]
source = ['pydantic']
branch = true
context = '${CONTEXT}'
[tool.coverage.report]
precision = 2
exclude_lines = [
'pragma: no cover',
'raise NotImplementedError',
'raise NotImplemented',
'if TYPE_CHECKING:',
'@overload',
]
[tool.coverage.paths]
source = [
'pydantic/',
'/Users/runner/work/pydantic/pydantic/pydantic/',
'D:\a\pydantic\pydantic\pydantic',
]
[tool.black]
color = true
line-length = 120
target-version = ['py310']
skip-string-normalization = true
[tool.isort]
line_length = 120
known_first_party = 'pydantic'
multi_line_output = 3
include_trailing_comma = true
force_grid_wrap = 0
combine_as_imports = true
[tool.mypy]
python_version = '3.10'
show_error_codes = true
follow_imports = 'silent'
strict_optional = true
warn_redundant_casts = true
warn_unused_ignores = true
disallow_any_generics = true
check_untyped_defs = true
no_implicit_reexport = true
warn_unused_configs = true
disallow_subclassing_any = true
disallow_incomplete_defs = true
disallow_untyped_decorators = true
disallow_untyped_calls = true
# for strict mypy: (this is the tricky one :-))
disallow_untyped_defs = true
# remaining arguments from `mypy --strict` which cause errors
# no_implicit_optional = true
# warn_return_any = true
# ansi2html and devtools are required to avoid the need to install these packages when running linting,
# they're used in the docs build script
[[tool.mypy.overrides]]
module = [
'email_validator.*',
'dotenv.*',
'toml.*',
'ansi2html.*',
'devtools.*',
]
ignore_missing_imports = true
-8
View File
@@ -1,8 +0,0 @@
# requirements for compilation and from setup.py so dependabot prompts us to test with latest version of these packages
Cython==0.29.32;sys_platform!='win32'
devtools==0.9.0
email-validator==1.2.1
dataclasses==0.6; python_version < '3.7'
typing-extensions==4.3.0
python-dotenv==0.20.0
+5
View File
@@ -0,0 +1,5 @@
-r ./pyproject-all.txt
-r ./docs.txt
-r ./linting.txt
-r ./testing.txt
-r ./testing-extra.txt
+16
View File
@@ -0,0 +1,16 @@
autoflake
ansi2html
devtools
flake8
flake8-quotes
flake8-pyproject
hypothesis
markdown-include
mdx-truly-sane-lists
mkdocs
mkdocs-exclude
mkdocs-material
pyupgrade
orjson
sqlalchemy
ujson
+119
View File
@@ -0,0 +1,119 @@
#
# This file is autogenerated by pip-compile with python 3.10
# To update, run:
#
# pip-compile --output-file=requirements/docs.txt requirements/docs.in
#
ansi2html==1.8.0
# via -r requirements/docs.in
asttokens==2.0.8
# via devtools
attrs==22.1.0
# via hypothesis
autoflake==1.5.3
# via -r requirements/docs.in
click==8.1.3
# via mkdocs
devtools==0.9.0
# via -r requirements/docs.in
exceptiongroup==1.0.0rc9
# via hypothesis
executing==0.10.0
# via devtools
flake8==5.0.4
# via
# -r requirements/docs.in
# flake8-pyproject
# flake8-quotes
flake8-pyproject==1.1.0.post0
# via -r requirements/docs.in
flake8-quotes==3.3.1
# via -r requirements/docs.in
ghp-import==2.1.0
# via mkdocs
greenlet==1.1.3
# via sqlalchemy
hypothesis==6.54.4
# via -r requirements/docs.in
importlib-metadata==4.12.0
# via mkdocs
jinja2==3.1.2
# via
# mkdocs
# mkdocs-material
markdown==3.3.7
# via
# markdown-include
# mdx-truly-sane-lists
# mkdocs
# mkdocs-material
# pymdown-extensions
markdown-include==0.7.0
# via -r requirements/docs.in
markupsafe==2.1.1
# via jinja2
mccabe==0.7.0
# via flake8
mdx-truly-sane-lists==1.3
# via -r requirements/docs.in
mergedeep==1.3.4
# via mkdocs
mkdocs==1.3.1
# via
# -r requirements/docs.in
# mkdocs-exclude
# mkdocs-material
mkdocs-exclude==1.0.2
# via -r requirements/docs.in
mkdocs-material==8.4.2
# via -r requirements/docs.in
mkdocs-material-extensions==1.0.3
# via mkdocs-material
orjson==3.8.0
# via -r requirements/docs.in
packaging==21.3
# via mkdocs
pycodestyle==2.9.1
# via flake8
pyflakes==2.5.0
# via
# autoflake
# flake8
pygments==2.13.0
# via mkdocs-material
pymdown-extensions==9.5
# via mkdocs-material
pyparsing==3.0.9
# via packaging
python-dateutil==2.8.2
# via ghp-import
pyupgrade==2.37.3
# via -r requirements/docs.in
pyyaml==6.0
# via
# mkdocs
# pyyaml-env-tag
pyyaml-env-tag==0.1
# via mkdocs
six==1.16.0
# via
# asttokens
# python-dateutil
sortedcontainers==2.4.0
# via hypothesis
sqlalchemy==1.4.40
# via -r requirements/docs.in
tokenize-rt==4.2.1
# via pyupgrade
toml==0.10.2
# via autoflake
tomli==2.0.1
# via flake8-pyproject
typing-extensions==4.3.0
# via -r requirements/docs.in
ujson==5.4.0
# via -r requirements/docs.in
watchdog==2.1.9
# via mkdocs
zipp==3.8.1
# via importlib-metadata
+9
View File
@@ -0,0 +1,9 @@
black
flake8
flake8-quotes
flake8-pyproject
hypothesis
isort
pyupgrade
mypy
pre-commit
+79
View File
@@ -0,0 +1,79 @@
#
# This file is autogenerated by pip-compile with python 3.10
# To update, run:
#
# pip-compile --output-file=requirements/linting.txt requirements/linting.in
#
attrs==22.1.0
# via hypothesis
black==22.8.0
# via -r requirements/linting.in
cfgv==3.3.1
# via pre-commit
click==8.1.3
# via black
distlib==0.3.6
# via virtualenv
exceptiongroup==1.0.0rc9
# via hypothesis
filelock==3.8.0
# via virtualenv
flake8==5.0.4
# via
# -r requirements/linting.in
# flake8-pyproject
# flake8-quotes
flake8-pyproject==1.1.0.post0
# via -r requirements/linting.in
flake8-quotes==3.3.1
# via -r requirements/linting.in
hypothesis==6.54.4
# via -r requirements/linting.in
identify==2.5.3
# via pre-commit
isort==5.10.1
# via -r requirements/linting.in
mccabe==0.7.0
# via flake8
mypy==0.971
# via -r requirements/linting.in
mypy-extensions==0.4.3
# via
# black
# mypy
nodeenv==1.7.0
# via pre-commit
pathspec==0.10.1
# via black
platformdirs==2.5.2
# via
# black
# virtualenv
pre-commit==2.20.0
# via -r requirements/linting.in
pycodestyle==2.9.1
# via flake8
pyflakes==2.5.0
# via flake8
pyupgrade==2.37.3
# via -r requirements/linting.in
pyyaml==6.0
# via pre-commit
sortedcontainers==2.4.0
# via hypothesis
tokenize-rt==4.2.1
# via pyupgrade
toml==0.10.2
# via pre-commit
tomli==2.0.1
# via
# black
# flake8-pyproject
# mypy
typing-extensions==4.3.0
# via mypy
virtualenv==20.16.4
# via pre-commit
# The following packages are considered to be unsafe in a requirements file:
# setuptools
+16
View File
@@ -0,0 +1,16 @@
#
# This file is autogenerated by pip-compile with python 3.10
# To update, run:
#
# pip-compile --extra=email,dotenv --output-file=requirements/pyproject-all.txt pyproject.toml
#
dnspython==2.2.1
# via email-validator
email-validator==1.2.1
# via pydantic (pyproject.toml)
idna==3.3
# via email-validator
python-dotenv==0.20.0
# via pydantic (pyproject.toml)
typing-extensions==4.3.0
# via pydantic (pyproject.toml)
+8
View File
@@ -0,0 +1,8 @@
#
# This file is autogenerated by pip-compile with python 3.10
# To update, run:
#
# pip-compile --output-file=requirements/pyproject-min.txt pyproject.toml
#
typing-extensions==4.3.0
# via pydantic (pyproject.toml)
+4
View File
@@ -0,0 +1,4 @@
devtools
hypothesis
mypy
typed-ast;python_version<'3.8'
+34
View File
@@ -0,0 +1,34 @@
#
# This file is autogenerated by pip-compile with python 3.7
# To update, run:
#
# pip-compile --output-file=requirements/testing-extra.txt requirements/testing-extra.in
#
asttokens==2.0.8
# via devtools
attrs==22.1.0
# via hypothesis
devtools==0.9.0
# via -r requirements/testing-extra.in
exceptiongroup==1.0.0rc9
# via hypothesis
executing==0.10.0
# via devtools
hypothesis==6.54.4
# via -r requirements/testing-extra.in
mypy==0.971
# via -r requirements/testing-extra.in
mypy-extensions==0.4.3
# via mypy
six==1.16.0
# via asttokens
sortedcontainers==2.4.0
# via hypothesis
tomli==2.0.1
# via mypy
typed-ast==1.5.4 ; python_version < "3.8"
# via
# -r requirements/testing-extra.in
# mypy
typing-extensions==4.3.0
# via mypy
+4
View File
@@ -0,0 +1,4 @@
coverage[toml]
pytest
pytest-mock
pytest-sugar
+45
View File
@@ -0,0 +1,45 @@
#
# This file is autogenerated by pip-compile with python 3.7
# To update, run:
#
# pip-compile --output-file=requirements/testing.txt requirements/testing.in
#
attrs==22.1.0
# via pytest
coverage[toml]==6.4.4
# via -r requirements/testing.in
importlib-metadata==4.12.0
# via
# pluggy
# pytest
iniconfig==1.1.1
# via pytest
packaging==21.3
# via
# pytest
# pytest-sugar
pluggy==1.0.0
# via pytest
py==1.11.0
# via pytest
pyparsing==3.0.9
# via packaging
pytest==7.1.3
# via
# -r requirements/testing.in
# pytest-mock
# pytest-sugar
pytest-mock==3.8.2
# via -r requirements/testing.in
pytest-sugar==0.9.5
# via -r requirements/testing.in
termcolor==1.1.0
# via pytest-sugar
tomli==2.0.1
# via
# coverage
# pytest
typing-extensions==4.3.0
# via importlib-metadata
zipp==3.8.1
# via importlib-metadata
-92
View File
@@ -1,92 +0,0 @@
[tool:pytest]
testpaths = tests
addopts = -p no:hypothesispytest
filterwarnings =
error
ignore::DeprecationWarning:distutils
ignore::DeprecationWarning:Cython
# for Python 3.10+: mypy still relies on distutils on windows. We hence ignore those warnings
ignore:The distutils package is deprecated and slated for removal in Python 3.12:DeprecationWarning
ignore:The distutils.sysconfig module is deprecated, use sysconfig instead:DeprecationWarning
# for Python 3.11
ignore:path is deprecated.*:DeprecationWarning:certifi
ignore:module 'sre_constants' is deprecated:DeprecationWarning:pkg_resources
[flake8]
max-line-length = 120
max-complexity = 14
inline-quotes = single
multiline-quotes = double
ignore = E203, W503
per-file-ignores =
docs/examples/schema_unenforced_constraints.py: F811
docs/examples/validation_decorator_async.py: E402
docs/examples/types_constrained.py: F722
[coverage:run]
source = pydantic
branch = True
context = ${CONTEXT}
[coverage:report]
precision = 2
exclude_lines =
pragma: no cover
raise NotImplementedError
raise NotImplemented
if TYPE_CHECKING:
@overload
[coverage:paths]
source =
pydantic/
/Users/runner/work/pydantic/pydantic/pydantic/
D:\a\pydantic\pydantic\pydantic
[isort]
line_length=120
known_first_party=pydantic
multi_line_output=3
include_trailing_comma=True
force_grid_wrap=0
combine_as_imports=True
[mypy]
python_version = 3.9
show_error_codes = True
follow_imports = silent
strict_optional = True
warn_redundant_casts = True
warn_unused_ignores = True
disallow_any_generics = True
check_untyped_defs = True
no_implicit_reexport = True
warn_unused_configs = True
disallow_subclassing_any = True
disallow_incomplete_defs = True
disallow_untyped_decorators = True
disallow_untyped_calls = True
# for strict mypy: (this is the tricky one :-))
disallow_untyped_defs = True
# remaining arguments from `mypy --strict` which cause errors
;no_implicit_optional = True
;warn_return_any = True
[mypy-email_validator]
ignore_missing_imports = true
[mypy-dotenv]
ignore_missing_imports = true
[mypy-toml]
ignore_missing_imports = true
# ansi2html and devtools are required to avoid the need to install these packages when running linting,
# they're used in the docs build script
[mypy-ansi2html]
ignore_missing_imports = true
[mypy-devtools]
ignore_missing_imports = true
+15 -130
View File
@@ -1,141 +1,26 @@
import os
import re
import sys
from importlib.machinery import SourceFileLoader
from pathlib import Path
from setuptools import setup
if os.name == 'nt':
from setuptools.command import build_ext
def get_export_symbols(self, ext):
"""
Slightly modified from:
https://github.com/python/cpython/blob/8849e5962ba481d5d414b3467a256aba2134b4da\
/Lib/distutils/command/build_ext.py#L686-L703
"""
# Patch from: https://bugs.python.org/issue35893
parts = ext.name.split('.')
if parts[-1] == '__init__':
suffix = parts[-2]
else:
suffix = parts[-1]
# from here on unchanged
try:
# Unicode module name support as defined in PEP-489
# https://www.python.org/dev/peps/pep-0489/#export-hook-name
suffix.encode('ascii')
except UnicodeEncodeError:
suffix = 'U' + suffix.encode('punycode').replace(b'-', b'_').decode('ascii')
initfunc_name = 'PyInit_' + suffix
if initfunc_name not in ext.export_symbols:
ext.export_symbols.append(initfunc_name)
return ext.export_symbols
build_ext.build_ext.get_export_symbols = get_export_symbols
sys.stderr.write(
"""
===============================
Unsupported installation method
===============================
pydantic no longer supports installation with `python setup.py install`.
Please use `python -m pip install .` instead.
"""
)
sys.exit(1)
class ReplaceLinks:
def __init__(self):
self.links = set()
def replace_issues(self, m):
id = m.group(1)
self.links.add(f'.. _#{id}: https://github.com/pydantic/pydantic/issues/{id}')
return f'`#{id}`_'
def replace_users(self, m):
name = m.group(2)
self.links.add(f'.. _@{name}: https://github.com/{name}')
return f'{m.group(1)}`@{name}`_'
def extra(self):
return '\n\n' + '\n'.join(sorted(self.links)) + '\n'
description = 'Data validation and settings management using python type hints'
THIS_DIR = Path(__file__).resolve().parent
try:
history = (THIS_DIR / 'HISTORY.md').read_text(encoding='utf-8')
history = re.sub(r'#(\d+)', r'[#\1](https://github.com/pydantic/pydantic/issues/\1)', history)
history = re.sub(r'( +)@([\w\-]+)', r'\1[@\2](https://github.com/\2)', history, flags=re.I)
history = re.sub('@@', '@', history)
long_description = (THIS_DIR / 'README.md').read_text(encoding='utf-8') + '\n\n' + history
except FileNotFoundError:
long_description = description + '.\n\nSee https://pydantic-docs.helpmanual.io/ for documentation.'
# avoid loading the package before requirements are installed:
version = SourceFileLoader('version', 'pydantic/version.py').load_module()
ext_modules = None
if not any(arg in sys.argv for arg in ['clean', 'check']) and 'SKIP_CYTHON' not in os.environ:
try:
from Cython.Build import cythonize
except ImportError:
pass
else:
# For cython test coverage install with `make build-trace`
compiler_directives = {}
if 'CYTHON_TRACE' in sys.argv:
compiler_directives['linetrace'] = True
# Set CFLAG to all optimizations (-O3)
# Any additional CFLAGS will be appended. Only the last optimization flag will have effect
os.environ['CFLAGS'] = '-O3 ' + os.environ.get('CFLAGS', '')
ext_modules = cythonize(
'pydantic/*.py',
exclude=['pydantic/generics.py'],
nthreads=int(os.getenv('CYTHON_NTHREADS', 0)),
language_level=3,
compiler_directives=compiler_directives,
)
# The below code will never execute, however GitHub is particularly
# picky about where it finds Python packaging metadata.
# See: https://github.com/github/feedback/discussions/6456
#
# To be removed once GitHub catches up.
setup(
name='pydantic',
version=str(version.VERSION),
description=description,
long_description=long_description,
long_description_content_type='text/markdown',
classifiers=[
'Development Status :: 5 - Production/Stable',
'Programming Language :: Python',
'Programming Language :: Python :: 3',
'Programming Language :: Python :: 3 :: Only',
'Programming Language :: Python :: 3.7',
'Programming Language :: Python :: 3.8',
'Programming Language :: Python :: 3.9',
'Programming Language :: Python :: 3.10',
'Programming Language :: Python :: 3.11',
'Intended Audience :: Developers',
'Intended Audience :: Information Technology',
'Intended Audience :: System Administrators',
'License :: OSI Approved :: MIT License',
'Operating System :: Unix',
'Operating System :: POSIX :: Linux',
'Environment :: Console',
'Environment :: MacOS X',
'Framework :: Hypothesis',
'Topic :: Software Development :: Libraries :: Python Modules',
'Topic :: Internet',
],
author='Samuel Colvin',
author_email='s@muelcolvin.com',
url='https://github.com/pydantic/pydantic',
license='MIT',
packages=['pydantic'],
package_data={'pydantic': ['py.typed']},
python_requires='>=3.7',
zip_safe=False, # https://mypy.readthedocs.io/en/latest/installed_packages.html
install_requires=[
'typing-extensions>=4.1.0'
],
extras_require={
'email': ['email-validator>=1.0.3'],
'dotenv': ['python-dotenv>=0.10.4'],
},
ext_modules=ext_modules,
entry_points={'hypothesis': ['_ = pydantic._hypothesis_plugin']},
)
-8
View File
@@ -9,14 +9,6 @@ from types import FunctionType
import pytest
from _pytest.assertion.rewrite import AssertionRewritingHook
# See https://hypothesis.readthedocs.io/en/latest/strategies.html#interaction-with-pytest-cov
try:
from hypothesis import given # noqa
except ImportError:
pytest_plugins = []
else:
pytest_plugins = ['hypothesis.extra.pytestplugin']
def _extract_source_code_from_function(function):
if function.__code__.co_argcount:
+1 -1
View File
@@ -1,2 +1,2 @@
22: error: Unsupported operand types for + ("int" and "str") [operator]
23: error: Unsupported operand types for + ("int" and "str") [operator]
23: error: Unsupported operand types for + ("int" and "str") [operator]
+1 -1
View File
@@ -1 +1 @@
20: error: "Model" has no attribute "foobar" [attr-defined]
20: error: "Model" has no attribute "foobar" [attr-defined]
+1 -1
View File
@@ -1 +1 @@
22: error: Argument 1 to "append" of "list" has incompatible type "str"; expected "int" [arg-type]
22: error: Argument 1 to "append" of "list" has incompatible type "str"; expected "int" [arg-type]
+1 -1
View File
@@ -5,4 +5,4 @@
14: error: Argument 2 to "foo" has incompatible type "int"; expected "str" [arg-type]
15: error: Unexpected keyword argument "d" for "foo" [call-arg]
17: error: "Callable[[int, DefaultNamedArg(str, 'c')], str]" has no attribute "raw_function" [attr-defined]
26: error: Incompatible types in assignment (expression has type "str", variable has type "int") [assignment]
26: error: Incompatible types in assignment (expression has type "str", variable has type "int") [assignment]
+4 -6
View File
@@ -1,6 +1,7 @@
import importlib
import os
import re
import sys
from pathlib import Path
import pytest
@@ -16,10 +17,7 @@ except ImportError:
parse_mypy_version = lambda _: (0,) # noqa: E731
try:
import dotenv
except ImportError:
dotenv = None
pytestmark = pytest.mark.skipif(sys.platform != 'linux' and 'CI' in os.environ, reason='only run on linux when on CI')
# This ensures mypy can find the test files, no matter where tests are run from:
os.chdir(Path(__file__).parent.parent.parent)
@@ -54,7 +52,7 @@ cases = [
executable_modules = list({fname[:-3] for _, fname, out_fname in cases if out_fname is None})
@pytest.mark.skipif(not (dotenv and mypy_api), reason='dotenv or mypy are not installed')
@pytest.mark.skipif(not mypy_api, reason='mypy is not installed')
@pytest.mark.parametrize('config_filename,python_filename,output_filename', cases)
def test_mypy_results(config_filename: str, python_filename: str, output_filename: str) -> None:
full_config_filename = f'tests/mypy/configs/{config_filename}'
@@ -96,7 +94,7 @@ def test_mypy_results(config_filename: str, python_filename: str, output_filenam
assert actual_out == expected_out, actual_out
@pytest.mark.skipif(not (dotenv and mypy_api), reason='dotenv or mypy are not installed')
@pytest.mark.skipif(not mypy_api, reason='mypy is not installed')
def test_bad_toml_config() -> None:
full_config_filename = 'tests/mypy/configs/pyproject-plugin-bad-param.toml'
full_filename = 'tests/mypy/modules/success.py'
-11
View File
@@ -1,11 +0,0 @@
black==22.8.0
flake8==5.0.4
flake8-quotes==3.3.1
hypothesis==6.54.4
isort==5.10.1
pyupgrade==2.37.3
mypy==0.971
pre-commit==2.20.0
pycodestyle==2.9.1
pyflakes==2.5.0
twine==4.0.1
-9
View File
@@ -1,9 +0,0 @@
coverage==6.4.4
hypothesis==6.54.4
# pin importlib-metadata as upper versions need typing-extensions to work if on Python < 3.8
importlib-metadata==3.1.0;python_version<"3.8"
mypy==0.971
pytest==7.1.2
pytest-cov==3.0.0
pytest-mock==3.8.2
pytest-sugar==0.9.5
+1 -32
View File
@@ -14,7 +14,6 @@ from pydantic import (
NoneStrBytes,
StrBytes,
ValidationError,
compiled,
constr,
errors,
validate_model,
@@ -22,11 +21,6 @@ from pydantic import (
)
from pydantic.fields import Field
try:
import cython
except ImportError:
cython = None
def test_str_bytes():
class Model(BaseModel):
@@ -244,9 +238,7 @@ def test_tuple_more():
(dict, frozenset, list, set, tuple, type),
],
)
@pytest.mark.skipif(
sys.version_info < (3, 9) or compiled, reason='PEP585 generics only supported for python 3.9 and above'
)
@pytest.mark.skipif(sys.version_info < (3, 9), reason='PEP585 generics only supported for python 3.9 and above')
def test_pep585_generic_types(dict_cls, frozenset_cls, list_cls, set_cls, tuple_cls, type_cls):
class Type1:
pass
@@ -1874,29 +1866,6 @@ def test_default_factory_validator_child():
assert Child(foo=['a', 'b']).foo == ['a-1', 'b-1']
@pytest.mark.skipif(cython is None, reason='cython not installed')
def test_cython_function_untouched():
Model = cython.inline(
# language=Python
"""
from pydantic import BaseModel
class Model(BaseModel):
a = 0.0
b = 10
def get_double_a(self) -> float:
return self.a + self.b
return Model
"""
)
model = Model(a=10.2)
assert model.a == 10.2
assert model.b == 10
return model.get_double_a() == 20.2
def test_resolve_annotations_module_missing(tmp_path):
# see https://github.com/pydantic/pydantic/issues/2363
file_path = tmp_path / 'module_to_load.py'
+1 -29
View File
@@ -1,17 +1,14 @@
import collections.abc
import os
import pickle
import re
import string
import sys
from copy import copy, deepcopy
from typing import Callable, Dict, ForwardRef, List, NewType, Tuple, TypeVar, Union
import pytest
from pkg_resources import safe_version
from typing_extensions import Annotated, Literal
from pydantic import VERSION, BaseModel, ConstrainedList, conlist
from pydantic import BaseModel, ConstrainedList, conlist
from pydantic.color import Color
from pydantic.dataclasses import dataclass
from pydantic.fields import Undefined
@@ -37,10 +34,8 @@ from pydantic.utils import (
path_type,
smart_deepcopy,
to_lower_camel,
truncate,
unique_list,
)
from pydantic.version import version_info
try:
import devtools
@@ -95,19 +90,6 @@ def test_lenient_issubclass_is_lenient():
assert lenient_issubclass('a', 'a') is False
@pytest.mark.parametrize(
'input_value,output',
[
(object, "<class 'object'>"),
(string.ascii_lowercase, "'abcdefghijklmnopq…'"),
(list(range(20)), '[0, 1, 2, 3, 4, 5, …'),
],
)
def test_truncate(input_value, output):
with pytest.warns(DeprecationWarning, match='`truncate` is no-longer used by pydantic and is deprecated'):
assert truncate(input_value, max_len=20) == output
@pytest.mark.parametrize(
'input_value,output',
[
@@ -372,16 +354,6 @@ def test_get_model():
get_model(C)
def test_version_info():
s = version_info()
assert re.match(' *pydantic version: ', s)
assert s.count('\n') == 5
def test_standard_version():
assert safe_version(VERSION) == VERSION
def test_class_attribute():
class Foo:
attr = ClassAttribute('attr', 'foo')
+16
View File
@@ -1,4 +1,20 @@
import re
from packaging.version import parse as parse_version
import pydantic
from pydantic.version import version_info
def test_version_info():
s = version_info()
assert re.match(' *pydantic version: ', s)
assert s.count('\n') == 4
def test_standard_version():
v = parse_version(pydantic.VERSION)
assert str(v) == pydantic.VERSION
def test_version_attribute_is_present():