Compare commits

...

44 Commits

Author SHA1 Message Date
Matthew Honnibal 66b1691dc2 Debug: show per-key manifest diffs in test_manifest_is_current
tests / Validate (push) Has been cancelled
tests / Test (macos-latest, 3.10) (push) Has been cancelled
tests / Test (macos-latest, 3.11) (push) Has been cancelled
tests / Test (macos-latest, 3.12) (push) Has been cancelled
tests / Test (macos-latest, 3.13) (push) Has been cancelled
tests / Test (macos-latest, 3.14) (push) Has been cancelled
tests / Test (ubuntu-latest, 3.10) (push) Has been cancelled
tests / Test (ubuntu-latest, 3.11) (push) Has been cancelled
tests / Test (ubuntu-latest, 3.12) (push) Has been cancelled
tests / Test (ubuntu-latest, 3.13) (push) Has been cancelled
tests / Test (ubuntu-latest, 3.14) (push) Has been cancelled
tests / Test (windows-latest, 3.10) (push) Has been cancelled
tests / Test (windows-latest, 3.11) (push) Has been cancelled
tests / Test (windows-latest, 3.12) (push) Has been cancelled
tests / Test (windows-latest, 3.13) (push) Has been cancelled
tests / Test (windows-latest, 3.14) (push) Has been cancelled
2026-03-23 16:12:02 +01:00
Matthew Honnibal 1041f8b426 Debug: dump manifest diff in test_manifest_is_current 2026-03-23 15:53:13 +01:00
Matthew Honnibal cb67fe175a Regenerate CLI manifest for typer 0.24.1 plain-text output 2026-03-23 15:19:01 +01:00
Matthew Honnibal ec786c85ad Add local lint script matching CI validate + mypy checks 2026-03-23 14:59:58 +01:00
Matthew Honnibal c5bcffdbf8 Fix import sorting (ruff I001) for CI validation 2026-03-23 14:58:26 +01:00
Matthew Honnibal 93547fe2e8 Fix lint issues across PR files
- setup.py: rename loop variable shadowing parameter (B020)
- _util.py: remove unused registry import (F401), use specific except clause (E722, B904)
- test_cli_app.py: use dict literals instead of dict() (C408)
- main.py: extract _try_static_group to reduce complexity (C901)
2026-03-23 14:56:27 +01:00
Matthew Honnibal 7bb0938725 Merge branch 'faster-cli' of https://github.com/explosion/spaCy into faster-cli 2026-03-23 14:50:07 +01:00
Matthew Honnibal 4967496dbd Update test_cli_launcher 2026-03-23 14:49:31 +01:00
Matthew Honnibal 8a318dbae5 Fix manifest 2026-03-23 14:49:31 +01:00
Matthew Honnibal f0abcf7bc0 Update manifest 2026-03-23 14:49:31 +01:00
Matthew Honnibal 0a45289512 Fix lazy load on modules where the function shadows 2026-03-23 14:49:31 +01:00
Matthew Honnibal 188c90d72a Add lazy spaCy CLI loading and static launcher 2026-03-23 14:49:31 +01:00
Matthew Honnibal f175a51e2d Fully migrate to Pydantic v2 (#13940)
Use confection v1.3 and Thinc v8.3.13, which implement custom validation logic in place of Pydantic, allowing us to properly adopt Pydantic v2 and provide full Python 3.14 support.

Our dependency tree used Pydantic v1 in unusual ways, and relied on behaviours that Pydantic v2 reformed. In the time since Pydantic v2 was released there were a few attempts to migrate over to it, but the task has been complicated by the fact that the confection library has a fairly tangled implementation and I had reduced availability for open-source work in 2024 and 2025.

Specifically, our library confection provides the extensible configuration system we use in spaCy and Thinc. The config system allows you to refer to values that will be supplied by arbitrary functions, that e.g. define some neural network model or its sublayers. The functionality in confection is complicated because we aggressively prioritised user experience in the specification, even if it required increased implementation complexity.

Confection's original implementation built a dynamic Pydantic v1 schema for function-supplied values ("promises"). We validate the schema before calling any promises, and then validate the schema again after calling all the promises and substituting in their values. The variable-interpolation system adds further difficulties to the implementation, and we have to do it all subclassing the Python built-in configparser, which ties us to implementation choices I'd do differently if I had a clean slate.

Here's one summary of Pydantic v1-specific behaviours that the migration to v2 particularly difficult for us. This particular summary was produced during a session with Claude Code Opus 4.6, so nuances of it might be wrong. The full history of attempts at doing this spans over different refactors separated by a few months at a time, so I don't have a full record of all the things that I struggled with. It's possible some details of this summary are incorrect though.

The core problem we kept hitting: Pydantic v2 compiles validation schemas upfront and has much stricter immutability. The whole session has been a series of workarounds for this:

```
 1. Schema mutation — v1 let you mutate __fields__ in place; v2 needs model_rebuild() which loses forward ref namespaces, or create_model subclasses which don't propagate to parent schemas.
 2. model_dump vs dict — v2 converts dataclasses to dicts, breaking resolved objects. Needed a custom _model_to_dict helper.
 3. model_construct drops extras — v2 silently drops fields with extra="forbid", needed manual workarounds.
 4. Strict coercion — v2 coerces ndarray to List[Floats1d] via iteration, needed strict=True.
 5. Forward refs — Every schema with TYPE_CHECKING imports needs model_rebuild() with the right namespace, and that breaks when confection re-rebuilds later.
In order to adjust for behavioural differences like this, I'd refactored confection to build the different versions of the schema in multiple passes, instead of building all the representations together as we'd been doing. However this refactor itself had problems, further complicating the migration.
```

~I've now bitten the bullet and rolled back the refactor I'd been attempting of confection, and instead replaced the Pydantic validation with custom logic. This allows Confection to remove Pydantic as a dependency entirely.~ Update: Actually I went back and got the refactor working. All much nicer now.

I've taken some lengths to explain this because migrating off a dependency after breaking changes can be a sensitive topic. I want to stress that the changes Pydantic made from v1 to v2 are very good, and I greatly appreciate them as a user of FastAPI in our services. It would be very bad for the ecosystem if Pydantic pinned themselves to exactly matching the behaviours they had in v1 just to avoid breaking support for the sort of thing we'd been doing. Instead users who were relying on those behaviours like us should just find some way to adapt --- either vendor the v1 version we need, or change our behaviours, or implement an alternative. I would have liked to do this sooner but we've ultimately gone with the third option.
2026-03-23 13:45:02 +01:00
Matthew Honnibal 24255bd1e2 Fix import sorting for ruff isort compliance 2026-03-21 08:44:43 +01:00
Matthew Honnibal 8e6bd6d1c5 Apply ruff formatting to 8 files 2026-03-21 08:43:52 +01:00
Matthew Honnibal 47b5504e90 Autofix autofixable things from ruff 2026-03-21 08:43:16 +01:00
Matthew Honnibal 86f7ce303a Limit CI ruff lint to isort-only checks for now 2026-03-21 08:41:25 +01:00
Matthew Honnibal 79b5f811bf Update CI validation workflow: replace black, isort, flake8 with ruff 2026-03-21 08:39:43 +01:00
Matthew Honnibal 32c4b638ae Format with ruff 2026-03-21 08:38:53 +01:00
Matthew Honnibal a7f629bb91 Fix ruff isort config: replace unsupported profile with equivalent settings 2026-03-21 08:38:28 +01:00
Matthew Honnibal adeb1620ec Remove W503 from ruff ignore list (not a valid ruff rule) 2026-03-21 08:38:28 +01:00
Matthew Honnibal fd99ed312b Replace black, isort, and flake8 with ruff for linting and formatting
- requirements.txt: remove black, isort, flake8; add ruff
- pyproject.toml: replace [tool.isort] with [tool.ruff] config
- setup.cfg: remove [flake8] section (rules moved to pyproject.toml)
- .pre-commit-config.yaml: replace black/flake8 hooks with ruff/ruff-format
2026-03-21 08:38:28 +01:00
Matthew Honnibal 501ccfd5fb Fix pydantic v2 pattern validation error counts and attributeruler type annotation
- Update expected error counts in test_pattern_validation.py for pydantic v2
  (v2 reports errors for all union members, increasing counts for OP and
  nested pattern validation)
- Fix AttributeRulerPatternType to include List[MatcherPatternType] in
  the union (v2 is strict about nested list-of-list-of-dict types that
  v1 accepted laxly)
2026-03-20 20:50:56 +01:00
Matthew Honnibal b8bade1a57 Update spaCy to pydantic v2 native API
- Replace pydantic.v1 compat imports with direct v2 imports
- Replace class Config with model_config = ConfigDict(...)
- Replace @validator with @field_validator
- Replace ConstrainedStr with constr()
- Replace min_items with min_length, allow_population_by_field_name
  with populate_by_name
- Add model_rebuild() calls in __init__.py for forward ref resolution
- Update test error type assertions for v2
2026-03-20 15:03:41 +01:00
Matthew Honnibal f835985f3f Increment version 2026-03-20 14:38:21 +01:00
Matthew Honnibal d5f67dc92d Update spaCy pydantic imports from v1 compat to v2 native API 2026-03-20 14:31:59 +01:00
Matthew Honnibal 3154ede82a Revert pydantic v2 migration, restore v1 compat imports 2026-03-20 14:09:37 +01:00
Matthew Honnibal 4f19800b4e Revert to weasel <0.5 2026-03-20 14:04:12 +01:00
Matthew Honnibal 60a19cb800 Revert to confection <1 and allow pydantic v1 2026-03-20 14:02:03 +01:00
Matthew Honnibal 2afc3fd41c Escape braces in TokenPatternOperatorMinMax regex for Rust regex engine 2026-03-20 11:34:12 +01:00
Matthew Honnibal d41afc2966 isort 2026-03-20 11:13:21 +01:00
Matthew Honnibal 21439ec09d Migrate from pydantic v1 to v2, require pydantic>=2.0.0
Replace all pydantic.v1 compat imports with direct pydantic v2 imports.
Migrate schemas to v2 API: ConfigDict instead of inner Config class,
field_validator instead of validator, RootModel instead of __root__,
model_dump() instead of dict(), model_validate() instead of parse_obj(),
Annotated[str, StringConstraints()] instead of ConstrainedStr,
min_length instead of min_items, populate_by_name instead of
allow_population_by_field_name.
2026-03-20 11:10:26 +01:00
Matthew Honnibal f22ff91476 Fix vuln scan by not calling test file requirements requirements.txt 2026-03-20 09:55:49 +01:00
Matthew Honnibal ed20f79abe Require confection 2026-03-20 09:43:20 +01:00
Matthew Honnibal c6c78d6c33 Allow use of uv as a fallback to pip in spacy download 2026-03-20 09:20:38 +01:00
Matthew Honnibal 2f6142b43e Require weasel 1.0 2026-03-20 09:17:28 +01:00
Sofie Van Landeghem 37b4a74fa7 Switch dependency back from typer-slim to typer (#13922)
* change typer-slim dependency to typer

* set rich_markup_mode to None to preserve behaviour
2026-03-20 09:14:19 +01:00
Matthew Honnibal 4168448124 Update test_cli_launcher 2026-03-20 09:12:08 +01:00
Kaushik Rajan cfa1d3a59a fix: ensure memory_zone cleanup runs on exception (#13924) (#13932)
Wrap yield in try/finally in StringStore.memory_zone and
Vocab.memory_zone so transient state is always cleaned up,
even when an exception propagates through the context manager.
2026-03-15 11:29:09 +01:00
Matthew Honnibal 5c559fc017 Fix manifest 2026-03-09 16:12:26 +01:00
Matthew Honnibal 126deace20 Update manifest 2026-03-09 14:32:41 +01:00
Matthew Honnibal c7d7a724f2 Fix lazy load on modules where the function shadows 2026-03-09 14:32:31 +01:00
Matthew Honnibal aa17eb96fd Add lazy spaCy CLI loading and static launcher 2026-03-09 13:12:10 +01:00
Matthew Honnibal 453732d32d Format (#13929) 2026-03-03 09:56:06 +01:00
152 changed files with 1950 additions and 726 deletions
+4 -1
View File
@@ -7,9 +7,12 @@ on:
# ** matches 'zero or more of any character'
- 'release-v[0-9]+.[0-9]+.[0-9]+**'
- 'prerelease-v[0-9]+.[0-9]+.[0-9]+**'
permissions: {}
jobs:
build_wheels:
uses: explosion/gha-cibuildwheel/.github/workflows/cibuildwheel.yml@main
uses: explosion/gha-cibuildwheel/.github/workflows/cibuildwheel.yml@2c98f757f13d112cf73fcf4b627249f1fffb5aae # main
permissions:
contents: write
actions: read
+7 -3
View File
@@ -6,6 +6,8 @@ on:
- created
- edited
permissions: {}
jobs:
explosion-bot:
if: github.repository_owner == 'explosion'
@@ -15,13 +17,15 @@ jobs:
env:
GITHUB_CONTEXT: ${{ toJson(github) }}
run: echo "$GITHUB_CONTEXT"
- uses: actions/checkout@v4
- uses: actions/setup-python@v4
- uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6
- uses: actions/setup-python@a309ff8b426b58ec0e2a45f0f869d46889d02405 # v6
- name: Install and run explosion-bot
run: |
pip install git+https://${{ secrets.EXPLOSIONBOT_TOKEN }}@github.com/explosion/explosion-bot
git config --global url."https://x-access-token:${EXPLOSIONBOT_TOKEN}@github.com/".insteadOf "https://github.com/"
pip install git+https://github.com/explosion/explosion-bot
python -m explosionbot
env:
EXPLOSIONBOT_TOKEN: ${{ secrets.EXPLOSIONBOT_TOKEN }}
INPUT_TOKEN: ${{ secrets.EXPLOSIONBOT_TOKEN }}
INPUT_BK_TOKEN: ${{ secrets.BUILDKITE_SECRET }}
ENABLED_COMMANDS: "test_gpu,test_slow,test_slow_gpu"
+5 -1
View File
@@ -11,12 +11,16 @@ on:
types:
- labeled
permissions: {}
jobs:
issue-manager:
permissions:
issues: write
if: github.repository_owner == 'explosion'
runs-on: ubuntu-latest
steps:
- uses: tiangolo/issue-manager@0.4.0
- uses: tiangolo/issue-manager@4d1b7e05935a404dc8337d30bd23be46be8bb8e5 # 0.4.0
with:
token: ${{ secrets.GITHUB_TOKEN }}
config: >
+1 -1
View File
@@ -16,7 +16,7 @@ jobs:
if: github.repository_owner == 'explosion'
runs-on: ubuntu-latest
steps:
- uses: dessant/lock-threads@v5
- uses: dessant/lock-threads@1bf7ec25051fe7c00bdd17e6a7cf3d7bfb7dc771 # v5
with:
process-only: 'issues'
issue-inactive-days: '30'
+3 -1
View File
@@ -8,6 +8,8 @@ on:
types:
- published
permissions: {}
jobs:
upload_pypi:
runs-on: ubuntu-latest
@@ -21,7 +23,7 @@ jobs:
# or, alternatively, upload to PyPI on every tag starting with 'v' (remove on: release above to use this)
# if: github.event_name == 'push' && startsWith(github.ref, 'refs/tags/v')
steps:
- uses: robinraju/release-downloader@v1
- uses: robinraju/release-downloader@daf26c55d821e836577a15f77d86ddc078948b05 # v1
with:
tag: ${{ github.event.release.tag_name }}
fileName: '*'
+4 -9
View File
@@ -5,21 +5,16 @@ on:
paths:
- "website/meta/universe.json"
permissions: {}
jobs:
build:
if: github.repository_owner == 'explosion'
runs-on: ubuntu-latest
steps:
- name: Dump GitHub context
env:
GITHUB_CONTEXT: ${{ toJson(github) }}
PR_NUMBER: ${{github.event.number}}
run: |
echo "$GITHUB_CONTEXT"
- uses: actions/checkout@v4
- uses: actions/setup-python@v4
- uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6
- uses: actions/setup-python@a309ff8b426b58ec0e2a45f0f869d46889d02405 # v6
with:
python-version: '3.10'
- name: Install Bernadette app dependency and send an alert
+15 -24
View File
@@ -19,6 +19,8 @@ on:
- "*.mdx"
- "website/**"
permissions: {}
jobs:
validate:
name: Validate
@@ -26,49 +28,38 @@ jobs:
runs-on: ubuntu-latest
steps:
- name: Check out repo
uses: actions/checkout@v4
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6
- name: Configure Python version
uses: actions/setup-python@v4
uses: actions/setup-python@a309ff8b426b58ec0e2a45f0f869d46889d02405 # v6
with:
python-version: "3.10"
- name: black
- name: ruff format
run: |
python -m pip install black -c requirements.txt
python -m black spacy --check
- name: isort
python -m pip install ruff -c requirements.txt
python -m ruff format spacy --check
- name: ruff isort
run: |
python -m pip install isort -c requirements.txt
python -m isort spacy --check
- name: flake8
run: |
python -m pip install flake8==5.0.4
python -m flake8 spacy --count --select=E901,E999,F821,F822,F823,W605 --show-source --statistics
# Unfortunately cython-lint isn't working after the shift to Cython 3.
#- name: cython-lint
# run: |
# python -m pip install cython-lint -c requirements.txt
# # E501: line too log, W291: trailing whitespace, E266: too many leading '#' for block comment
# cython-lint spacy --ignore E501,W291,E266
python -m ruff check spacy --select I
tests:
name: Test
needs: Validate
strategy:
fail-fast: true
fail-fast: false
matrix:
os: [ubuntu-latest, windows-latest, macos-latest]
python_version: ["3.10", "3.11", "3.12", "3.13"]
python_version: ["3.10", "3.11", "3.12", "3.13", "3.14"]
runs-on: ${{ matrix.os }}
steps:
- name: Check out repo
uses: actions/checkout@v4
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6
- name: Configure Python version
uses: actions/setup-python@v4
uses: actions/setup-python@a309ff8b426b58ec0e2a45f0f869d46889d02405 # v6
with:
python-version: ${{ matrix.python_version }}
@@ -104,7 +95,7 @@ jobs:
shell: bash
- name: Test import
run: python -W error -c "import spacy"
run: python -W error -W 'ignore:Core Pydantic V1:UserWarning:pydantic' -c "import spacy"
- name: "Test download CLI"
run: |
@@ -165,7 +156,7 @@ jobs:
- name: "Run CPU tests"
run: |
python -m pytest --pyargs spacy -W error
python -m pytest --pyargs spacy -W error -W 'ignore:Core Pydantic V1:UserWarning:pydantic'
if: "!(startsWith(matrix.os, 'macos') && matrix.python_version == '3.11')"
- name: "Run CPU tests with thinc-apple-ops"
+4 -2
View File
@@ -13,6 +13,8 @@ on:
paths:
- "website/meta/universe.json"
permissions: {}
jobs:
validate:
name: Validate
@@ -20,10 +22,10 @@ jobs:
runs-on: ubuntu-latest
steps:
- name: Check out repo
uses: actions/checkout@v4
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6
- name: Configure Python version
uses: actions/setup-python@v4
uses: actions/setup-python@a309ff8b426b58ec0e2a45f0f869d46889d02405 # v6
with:
python-version: "3.7"
+5 -11
View File
@@ -1,13 +1,7 @@
repos:
- repo: https://github.com/ambv/black
rev: 22.3.0
- repo: https://github.com/astral-sh/ruff-pre-commit
rev: v0.9.0
hooks:
- id: black
language_version: python3.7
additional_dependencies: ['click==8.0.4']
- repo: https://github.com/pycqa/flake8
rev: 5.0.4
hooks:
- id: flake8
args:
- "--config=setup.cfg"
- id: ruff
args: ['--fix']
- id: ruff-format
+1
View File
@@ -1,4 +1,5 @@
recursive-include spacy *.pyi *.pyx *.pxd *.txt *.cfg *.jinja *.toml *.hh
recursive-include spacy_cli *.json
include LICENSE
include README.md
include pyproject.toml
Executable
+37
View File
@@ -0,0 +1,37 @@
#!/usr/bin/env bash
# Local lint script matching the CI Validate job + mypy type checks.
# Fixes formatting and import sorting in-place, then re-verifies in
# check mode to catch any conflicts between the two, and runs mypy.
set -euo pipefail
err=0
echo "==> ruff format (auto-fixing)"
python -m ruff format spacy
echo "==> ruff isort (auto-fixing)"
python -m ruff check spacy --select I --fix
echo "==> ruff format (verify)"
if ! python -m ruff format spacy --check; then
echo "FAIL: isort fix broke formatting"
err=1
fi
echo "==> ruff isort (verify)"
if ! python -m ruff check spacy --select I; then
echo "FAIL: format fix broke import sorting"
err=1
fi
echo "==> mypy"
if ! python -m mypy spacy; then
err=1
fi
if [ "$err" -ne 0 ]; then
echo "FAIL: see errors above"
exit 1
fi
echo "OK: all checks passed"
+11 -3
View File
@@ -5,7 +5,7 @@ requires = [
"cymem>=2.0.2,<2.1.0",
"preshed>=3.0.2,<3.1.0",
"murmurhash>=0.28.0,<1.1.0",
"thinc>=8.3.4,<8.4.0",
"thinc>=8.3.12,<8.4.0",
"numpy>=2.0.0,<3.0.0"
]
build-backend = "setuptools.build_meta"
@@ -62,5 +62,13 @@ repair-wheel-command = "delocate-wheel --require-archs {delocate_archs} -w {dest
[tool.cibuildwheel.pyodide]
[tool.isort]
profile = "black"
[tool.ruff]
line-length = 88
[tool.ruff.lint]
select = ["E", "F", "W", "C", "B", "B9"]
ignore = ["E203", "E266", "E501", "E731", "E741", "F541"]
[tool.ruff.lint.isort]
combine-as-imports = true
split-on-trailing-comma = true
+8 -9
View File
@@ -3,19 +3,19 @@ spacy-legacy>=3.0.11,<3.1.0
spacy-loggers>=1.0.0,<2.0.0
cymem>=2.0.2,<2.1.0
preshed>=3.0.2,<3.1.0
thinc>=8.3.4,<8.4.0
ml_datasets>=0.2.0,<0.3.0
thinc>=8.3.12,<8.4.0
ml_datasets>=0.2.1,<0.3.0
murmurhash>=0.28.0,<1.1.0
wasabi>=0.9.1,<1.2.0
srsly>=2.4.3,<3.0.0
srsly>=2.5.3,<3.0.0
catalogue>=2.0.6,<2.1.0
typer-slim>=0.3.0,<1.0.0
weasel>=0.4.2,<0.5.0
typer>=0.3.0,<1.0.0
weasel>=1.0.0,<2.0.0
# Third party dependencies
numpy>=2.0.0,<3.0.0
requests>=2.13.0,<3.0.0
tqdm>=4.38.0,<5.0.0
pydantic>=1.7.4,!=1.8,!=1.8.1,<3.0.0
pydantic>=2.0.0,<3.0.0
jinja2
# Official Python utilities
setuptools
@@ -26,13 +26,12 @@ cython>=3.0,<4.0
pytest>=5.2.0,!=7.1.0
pytest-timeout>=1.3.0,<2.0.0
mock>=2.0.0,<3.0.0
flake8>=3.8.0,<6.0.0
hypothesis>=3.27.0,<7.0.0
mypy>=1.5.0,<1.6.0; platform_machine != "aarch64" and python_version >= "3.8"
types-mock>=0.1.1
types-setuptools>=57.0.0
types-requests
types-setuptools>=57.0.0
black>=25.0.0
ruff>=0.9.0
cython-lint>=0.15.0
isort>=5.0,<6.0
confection>=1.1.0,<2.0.0
+12 -17
View File
@@ -22,6 +22,7 @@ classifiers =
Programming Language :: Python :: 3.11
Programming Language :: Python :: 3.12
Programming Language :: Python :: 3.13
Programming Language :: Python :: 3.14
Topic :: Scientific/Engineering
project_urls =
Release notes = https://github.com/explosion/spaCy/releases
@@ -41,7 +42,7 @@ setup_requires =
cymem>=2.0.2,<2.1.0
preshed>=3.0.2,<3.1.0
murmurhash>=0.28.0,<1.1.0
thinc>=8.3.4,<8.4.0
thinc>=8.3.12,<8.4.0
install_requires =
# Our libraries
spacy-legacy>=3.0.11,<3.1.0
@@ -49,18 +50,19 @@ install_requires =
murmurhash>=0.28.0,<1.1.0
cymem>=2.0.2,<2.1.0
preshed>=3.0.2,<3.1.0
thinc>=8.3.4,<8.4.0
thinc>=8.3.12,<8.4.0
wasabi>=0.9.1,<1.2.0
srsly>=2.4.3,<3.0.0
srsly>=2.5.3,<3.0.0
catalogue>=2.0.6,<2.1.0
weasel>=0.4.2,<0.5.0
weasel>=1.0.0,<2.0.0
confection>=1.1.0,<2.0.0
# Third-party dependencies
typer-slim>=0.3.0,<1.0.0
typer>=0.3.0,<1.0.0
tqdm>=4.38.0,<5.0.0
numpy>=1.15.0; python_version < "3.9"
numpy>=1.19.0; python_version >= "3.9"
requests>=2.13.0,<3.0.0
pydantic>=1.7.4,!=1.8,!=1.8.1,<3.0.0
pydantic>=2.0.0,<3.0.0
jinja2
# Official Python utilities
setuptools
@@ -68,7 +70,7 @@ install_requires =
[options.entry_points]
console_scripts =
spacy = spacy.cli:setup_cli
spacy = spacy_cli.main:main
[options.extras_require]
lookups =
@@ -130,20 +132,13 @@ universal = false
[sdist]
formats = gztar
[flake8]
ignore = E203, E266, E501, E731, W503, E741, F541
max-line-length = 80
select = B,C,E,F,W,T4,B9
exclude =
.env,
.git,
__pycache__,
_tokenizer_exceptions_list.py,
[tool:pytest]
markers =
slow: mark a test as slow
issue: reference specific issue
filterwarnings =
error
ignore:Core Pydantic V1:UserWarning:pydantic
[mypy]
ignore_missing_imports = True
+11 -11
View File
@@ -82,9 +82,9 @@ COMPILER_DIRECTIVES = {
}
# Files to copy into the package that are otherwise not included
COPY_FILES = {
ROOT / "setup.cfg": PACKAGE_ROOT / "tests" / "package",
ROOT / "pyproject.toml": PACKAGE_ROOT / "tests" / "package",
ROOT / "requirements.txt": PACKAGE_ROOT / "tests" / "package",
ROOT / "setup.cfg": PACKAGE_ROOT / "tests" / "package" / "test.cfg",
ROOT / "pyproject.toml": PACKAGE_ROOT / "tests" / "package" / "test.toml",
ROOT / "requirements.txt": PACKAGE_ROOT / "tests" / "package" / "test.txt",
}
@@ -158,10 +158,10 @@ GIT_VERSION = "%(git_version)s"
def clean(path):
for path in path.glob("**/*"):
if path.is_file() and path.suffix in (".so", ".cpp", ".html"):
print(f"Deleting {path.name}")
path.unlink()
for child in path.glob("**/*"):
if child.is_file() and child.suffix in (".so", ".cpp", ".html"):
print(f"Deleting {child.name}")
child.unlink()
def setup_package():
@@ -173,10 +173,10 @@ def setup_package():
about = {}
exec(f.read(), about)
for copy_file, target_dir in COPY_FILES.items():
for copy_file, target_file in COPY_FILES.items():
if copy_file.exists():
shutil.copy(str(copy_file), str(target_dir))
print(f"Copied {copy_file} -> {target_dir}")
shutil.copyfile(str(copy_file), str(target_file))
print(f"Copied {copy_file} -> {target_file}")
include_dirs = [
numpy.get_include(),
@@ -213,7 +213,7 @@ def setup_package():
version=about["__version__"],
ext_modules=ext_modules,
cmdclass={"build_ext": build_ext_subclass},
package_data={"": ["*.pyx", "*.pxd", "*.pxi"]},
package_data={"": ["*.pyx", "*.pxd", "*.pxi"], "spacy_cli": ["*.json"]},
)
+24 -2
View File
@@ -10,17 +10,39 @@ setup_default_warnings() # noqa: E402
# These are imported as part of the API
from thinc.api import Config, prefer_gpu, require_cpu, require_gpu # noqa: F401
from . import pipeline # noqa: F401
from . import util
from . import (
pipeline, # noqa: F401
util,
)
from .about import __version__ # noqa: F401
from .cli.info import info # noqa: F401
from .errors import Errors
from .glossary import explain # noqa: F401
from .language import Language
from .registrations import REGISTRY_POPULATED, populate_registry
# Rebuild pydantic v2 schemas that use forward references to Language/Vocab
from .schemas import ( # noqa: F401
ConfigSchema,
ConfigSchemaInit,
ConfigSchemaNlp,
ConfigSchemaPretrain,
ConfigSchemaTraining,
)
from .training import Example # noqa: F401
from .util import logger, registry # noqa: F401
from .vocab import Vocab
_rebuild_ns = {"Language": Language, "Vocab": Vocab, "Example": Example}
for _schema in (
ConfigSchemaTraining,
ConfigSchemaNlp,
ConfigSchemaPretrain,
ConfigSchemaInit,
ConfigSchema,
):
_schema.model_rebuild(_types_namespace=_rebuild_ns) # type: ignore[attr-defined]
if sys.maxunicode == 65535:
raise SystemError(Errors.E130)
+1 -1
View File
@@ -1,5 +1,5 @@
# fmt: off
__title__ = "spacy"
__version__ = "3.8.11"
__version__ = "3.8.12"
__download_url__ = "https://github.com/explosion/spacy-models/releases/download"
__compatibility__ = "https://raw.githubusercontent.com/explosion/spacy-models/master/compatibility.json"
+90 -34
View File
@@ -1,40 +1,96 @@
import sys
import types
from importlib import import_module
from typing import Iterable
from typer.main import get_command
from wasabi import msg
# Needed for testing
from . import download as download_module # noqa: F401
from ._util import app, setup_cli # noqa: F401
from .apply import apply # noqa: F401
from .assemble import assemble_cli # noqa: F401
# These are the actual functions, NOT the wrapped CLI commands. The CLI commands
# are registered automatically and won't have to be imported here.
from .benchmark_speed import benchmark_speed_cli # noqa: F401
from .convert import convert # noqa: F401
from .debug_config import debug_config # noqa: F401
from .debug_data import debug_data # noqa: F401
from .debug_diff import debug_diff # noqa: F401
from .debug_model import debug_model # noqa: F401
from .download import download # noqa: F401
from .evaluate import evaluate # noqa: F401
from .find_function import find_function # noqa: F401
from .find_threshold import find_threshold # noqa: F401
from .info import info # noqa: F401
from .init_config import fill_config, init_config # noqa: F401
from .init_pipeline import init_pipeline_cli # noqa: F401
from .package import package # noqa: F401
from .pretrain import pretrain # noqa: F401
from .profile import profile # noqa: F401
from .project.assets import project_assets # type: ignore[attr-defined] # noqa: F401
from .project.clone import project_clone # type: ignore[attr-defined] # noqa: F401
from .project.document import ( # type: ignore[attr-defined] # noqa: F401
project_document,
from ..util import registry
from ._dispatch import (
GROUP_MODULES,
PUBLIC_ATTRS,
SUBCOMMAND_MODULES,
TOP_LEVEL_MODULES,
iter_builtin_modules,
)
from .project.dvc import project_update_dvc # type: ignore[attr-defined] # noqa: F401
from .project.pull import project_pull # type: ignore[attr-defined] # noqa: F401
from .project.push import project_push # type: ignore[attr-defined] # noqa: F401
from .project.run import project_run # type: ignore[attr-defined] # noqa: F401
from .train import train_cli # type: ignore[attr-defined] # noqa: F401
from .validate import validate # type: ignore[attr-defined] # noqa: F401
from ._util import COMMAND, add_project_cli, app
HELP_OPTIONS = {"--help", "-h"}
ROOT_OPTIONS = HELP_OPTIONS | {"--install-completion", "--show-completion"}
__all__ = [
"app",
"load_all_commands",
"load_for_argv",
"setup_cli",
*sorted(PUBLIC_ATTRS),
]
def _import_modules(module_names: Iterable[str]) -> None:
for module_name in module_names:
import_module(module_name)
def load_all_commands() -> None:
_import_modules(iter_builtin_modules())
add_project_cli()
def load_for_argv(argv: Iterable[str]) -> None:
args = list(argv)
if not args or args[0] in ROOT_OPTIONS or args[0].startswith("-"):
load_all_commands()
return
command = args[0]
if command == "project":
add_project_cli()
return
if command in GROUP_MODULES:
subcommand = args[1] if len(args) > 1 and not args[1].startswith("-") else None
if subcommand is not None and (command, subcommand) in SUBCOMMAND_MODULES:
_import_modules(SUBCOMMAND_MODULES[(command, subcommand)])
return
_import_modules(GROUP_MODULES[command])
return
if command in TOP_LEVEL_MODULES:
_import_modules(TOP_LEVEL_MODULES[command])
def setup_cli() -> None:
# Make sure entry-point CLI integrations are imported before command dispatch.
registry.cli.get_all()
load_for_argv(sys.argv[1:])
command = get_command(app)
command(prog_name=COMMAND)
def __getattr__(name: str):
if name not in PUBLIC_ATTRS:
raise AttributeError(f"module 'spacy.cli' has no attribute {name!r}")
module_name, attr_name = PUBLIC_ATTRS[name]
module = import_module(module_name)
value = module if attr_name is None else getattr(module, attr_name)
globals()[name] = value
return value
def __dir__():
return sorted(set(globals()) | set(PUBLIC_ATTRS))
class _CLIModule(types.ModuleType):
def __setattr__(self, name, value):
if isinstance(value, types.ModuleType) and name in PUBLIC_ATTRS:
_, attr_name = PUBLIC_ATTRS[name]
if attr_name is not None:
super().__setattr__(name, getattr(value, attr_name))
return
super().__setattr__(name, value)
sys.modules[__name__].__class__ = _CLIModule
@app.command("link", no_args_is_help=True, deprecated=True, hidden=True)
+104
View File
@@ -0,0 +1,104 @@
from typing import Dict, Iterable, Optional, Tuple
CommandPath = Tuple[str, ...]
TOP_LEVEL_MODULES: Dict[str, Tuple[str, ...]] = {
"apply": ("spacy.cli.apply",),
"assemble": ("spacy.cli.assemble",),
"convert": ("spacy.cli.convert",),
"debug-data": ("spacy.cli.debug_data",),
"download": ("spacy.cli.download",),
"evaluate": ("spacy.cli.evaluate",),
"find-function": ("spacy.cli.find_function",),
"find-threshold": ("spacy.cli.find_threshold",),
"info": ("spacy.cli.info",),
"package": ("spacy.cli.package",),
"pretrain": ("spacy.cli.pretrain",),
"profile": ("spacy.cli.profile",),
"train": ("spacy.cli.train",),
"validate": ("spacy.cli.validate",),
}
GROUP_MODULES: Dict[str, Tuple[str, ...]] = {
"benchmark": (
"spacy.cli.benchmark_speed",
"spacy.cli.evaluate",
),
"debug": (
"spacy.cli.debug_config",
"spacy.cli.debug_data",
"spacy.cli.debug_diff",
"spacy.cli.debug_model",
"spacy.cli.profile",
),
"init": (
"spacy.cli.init_config",
"spacy.cli.init_pipeline",
),
}
SUBCOMMAND_MODULES: Dict[CommandPath, Tuple[str, ...]] = {
("benchmark", "accuracy"): ("spacy.cli.evaluate",),
("benchmark", "speed"): ("spacy.cli.benchmark_speed",),
("debug", "config"): ("spacy.cli.debug_config",),
("debug", "data"): ("spacy.cli.debug_data",),
("debug", "diff-config"): ("spacy.cli.debug_diff",),
("debug", "model"): ("spacy.cli.debug_model",),
("debug", "profile"): ("spacy.cli.profile",),
("init", "config"): ("spacy.cli.init_config",),
("init", "fill-config"): ("spacy.cli.init_config",),
("init", "labels"): ("spacy.cli.init_pipeline",),
("init", "nlp"): ("spacy.cli.init_pipeline",),
("init", "vectors"): ("spacy.cli.init_pipeline",),
}
PUBLIC_ATTRS: Dict[str, Tuple[str, Optional[str]]] = {
"app": ("spacy.cli._util", "app"),
"apply": ("spacy.cli.apply", "apply"),
"assemble_cli": ("spacy.cli.assemble", "assemble_cli"),
"benchmark_speed_cli": ("spacy.cli.benchmark_speed", "benchmark_speed_cli"),
"convert": ("spacy.cli.convert", "convert"),
"debug_config": ("spacy.cli.debug_config", "debug_config"),
"debug_data": ("spacy.cli.debug_data", "debug_data"),
"debug_diff": ("spacy.cli.debug_diff", "debug_diff"),
"debug_model": ("spacy.cli.debug_model", "debug_model"),
"download": ("spacy.cli.download", "download"),
"download_module": ("spacy.cli.download", None),
"evaluate": ("spacy.cli.evaluate", "evaluate"),
"fill_config": ("spacy.cli.init_config", "fill_config"),
"find_function": ("spacy.cli.find_function", "find_function"),
"find_threshold": ("spacy.cli.find_threshold", "find_threshold"),
"info": ("spacy.cli.info", "info"),
"init_config": ("spacy.cli.init_config", "init_config"),
"init_pipeline_cli": ("spacy.cli.init_pipeline", "init_pipeline_cli"),
"package": ("spacy.cli.package", "package"),
"pretrain": ("spacy.cli.pretrain", "pretrain"),
"profile": ("spacy.cli.profile", "profile"),
"project_assets": ("spacy.cli.project.assets", "project_assets"),
"project_clone": ("spacy.cli.project.clone", "project_clone"),
"project_document": ("spacy.cli.project.document", "project_document"),
"project_pull": ("spacy.cli.project.pull", "project_pull"),
"project_push": ("spacy.cli.project.push", "project_push"),
"project_run": ("spacy.cli.project.run", "project_run"),
"project_update_dvc": ("spacy.cli.project.dvc", "project_update_dvc"),
"train_cli": ("spacy.cli.train", "train_cli"),
"validate": ("spacy.cli.validate", "validate"),
}
def iter_builtin_modules() -> Iterable[str]:
seen = set()
for modules in TOP_LEVEL_MODULES.values():
for module in modules:
if module not in seen:
seen.add(module)
yield module
for modules in GROUP_MODULES.values():
for module in modules:
if module not in seen:
seen.add(module)
yield module
+13 -23
View File
@@ -1,15 +1,11 @@
import hashlib
import os
import shutil
import sys
from configparser import InterpolationError
from contextlib import contextmanager
from pathlib import Path
from typing import (
TYPE_CHECKING,
Any,
Dict,
Iterable,
List,
Optional,
Tuple,
@@ -21,23 +17,15 @@ import srsly
import typer
from click import NoSuchOption
from click.shell_completion import split_arg_string
from thinc.api import Config, ConfigValidationError, require_gpu
from thinc.api import ConfigValidationError, require_gpu
from thinc.util import gpu_is_available
from typer.main import get_command
from wasabi import Printer, msg
from weasel import app as project_cli
from .. import about
from ..compat import Literal
from ..schemas import validate
from ..util import (
ENV_VARS,
SimpleFrozenDict,
import_file,
is_compatible_version,
logger,
make_tempdir,
registry,
run_command,
)
@@ -68,23 +56,25 @@ INIT_HELP = """Commands for initializing configs and pipeline packages."""
Arg = typer.Argument
Opt = typer.Option
app = typer.Typer(name=NAME, help=HELP)
app = typer.Typer(name=NAME, help=HELP, rich_markup_mode=None)
benchmark_cli = typer.Typer(name="benchmark", help=BENCHMARK_HELP, no_args_is_help=True)
debug_cli = typer.Typer(name="debug", help=DEBUG_HELP, no_args_is_help=True)
init_cli = typer.Typer(name="init", help=INIT_HELP, no_args_is_help=True)
_PROJECT_CLI_ADDED = False
app.add_typer(project_cli, name="project", help=PROJECT_HELP, no_args_is_help=True)
app.add_typer(debug_cli)
app.add_typer(benchmark_cli)
app.add_typer(init_cli)
def setup_cli() -> None:
# Make sure the entry-point for CLI runs, so that they get imported.
registry.cli.get_all()
# Ensure that the help messages always display the correct prompt
command = get_command(app)
command(prog_name=COMMAND)
def add_project_cli() -> None:
global _PROJECT_CLI_ADDED
if _PROJECT_CLI_ADDED:
return
from weasel import app as project_cli
app.add_typer(project_cli, name="project", help=PROJECT_HELP, no_args_is_help=True)
_PROJECT_CLI_ADDED = True
def parse_config_overrides(
@@ -215,8 +205,8 @@ def get_git_version(
"""
try:
ret = run_command("git --version", capture=True)
except:
raise RuntimeError(error)
except Exception as err:
raise RuntimeError(error) from err
stdout = ret.stdout.strip()
if not stdout or not stdout.startswith("git version"):
return 0, 0
+9 -6
View File
@@ -22,7 +22,7 @@ to be grabbed ("text" by default)."""
out_help = "Path to save the resulting .spacy file"
code_help = (
"Path to Python file with additional " "code (registered functions) to be imported"
"Path to Python file with additional code (registered functions) to be imported"
)
gold_help = "Use gold preprocessing provided in the .spacy files"
force_msg = (
@@ -72,11 +72,15 @@ def apply_cli(
data_path: Path = Arg(..., help=path_help, exists=True),
output_file: Path = Arg(..., help=out_help, dir_okay=False),
code_path: Optional[Path] = Opt(None, "--code", "-c", help=code_help),
text_key: str = Opt("text", "--text-key", "-tk", help="Key containing text string for JSONL"),
force_overwrite: bool = Opt(False, "--force", "-F", help="Force overwriting the output file"),
text_key: str = Opt(
"text", "--text-key", "-tk", help="Key containing text string for JSONL"
),
force_overwrite: bool = Opt(
False, "--force", "-F", help="Force overwriting the output file"
),
use_gpu: int = Opt(-1, "--gpu-id", "-g", help="GPU ID or -1 for CPU."),
batch_size: int = Opt(1, "--batch-size", "-b", help="Batch size."),
n_process: int = Opt(1, "--n-process", "-n", help="number of processors to use.")
n_process: int = Opt(1, "--n-process", "-n", help="number of processors to use."),
):
"""
Apply a trained pipeline to documents to get predictions.
@@ -114,8 +118,7 @@ def apply(
if len(paths) == 0:
docbin.to_disk(output_file)
msg.warn(
"Did not find data to process,"
f" {data_path} seems to be an empty directory."
f"Did not find data to process, {data_path} seems to be an empty directory."
)
return
nlp = load_model(model)
+19 -4
View File
@@ -24,10 +24,25 @@ from ._util import (
def assemble_cli(
# fmt: off
ctx: typer.Context, # This is only used to read additional arguments
config_path: Path = Arg(..., help="Path to config file", exists=True, allow_dash=True),
output_path: Path = Arg(..., help="Output directory to store assembled pipeline in"),
code_path: Optional[Path] = Opt(None, "--code", "-c", help="Path to Python file with additional code (registered functions) to be imported"),
verbose: bool = Opt(False, "--verbose", "-V", "-VV", help="Display more information for debugging purposes"),
config_path: Path = Arg(
..., help="Path to config file", exists=True, allow_dash=True
),
output_path: Path = Arg(
..., help="Output directory to store assembled pipeline in"
),
code_path: Optional[Path] = Opt(
None,
"--code",
"-c",
help="Path to Python file with additional code (registered functions) to be imported",
),
verbose: bool = Opt(
False,
"--verbose",
"-V",
"-VV",
help="Display more information for debugging purposes",
),
# fmt: on
):
"""
+22 -6
View File
@@ -24,13 +24,29 @@ def benchmark_speed_cli(
# fmt: off
ctx: typer.Context,
model: str = Arg(..., help="Model name or path"),
data_path: Path = Arg(..., help="Location of binary evaluation data in .spacy format", exists=True),
batch_size: Optional[int] = Opt(None, "--batch-size", "-b", min=1, help="Override the pipeline batch size"),
data_path: Path = Arg(
..., help="Location of binary evaluation data in .spacy format", exists=True
),
batch_size: Optional[int] = Opt(
None, "--batch-size", "-b", min=1, help="Override the pipeline batch size"
),
no_shuffle: bool = Opt(False, "--no-shuffle", help="Do not shuffle benchmark data"),
use_gpu: int = Opt(-1, "--gpu-id", "-g", help="GPU ID or -1 for CPU"),
n_batches: int = Opt(50, "--batches", help="Minimum number of batches to benchmark", min=30,),
warmup_epochs: int = Opt(3, "--warmup", "-w", min=0, help="Number of iterations over the data for warmup"),
code_path: Optional[Path] = Opt(None, "--code", "-c", help="Path to Python file with additional code (registered functions) to be imported"),
n_batches: int = Opt(
50,
"--batches",
help="Minimum number of batches to benchmark",
min=30,
),
warmup_epochs: int = Opt(
3, "--warmup", "-w", min=0, help="Number of iterations over the data for warmup"
),
code_path: Optional[Path] = Opt(
None,
"--code",
"-c",
help="Path to Python file with additional code (registered functions) to be imported",
),
# fmt: on
):
"""
@@ -151,7 +167,7 @@ def print_mean_with_ci(sample: numpy.ndarray):
low = bootstrap_means[int(len(bootstrap_means) * 0.025)]
high = bootstrap_means[int(len(bootstrap_means) * 0.975)]
print(f"Mean: {mean:.1f} words/s (95% CI: {low-mean:.1f} +{high-mean:.1f})")
print(f"Mean: {mean:.1f} words/s (95% CI: {low - mean:.1f} +{high - mean:.1f})")
def print_outliers(sample: numpy.ndarray):
+41 -11
View File
@@ -48,17 +48,47 @@ class FileTypes(str, Enum):
def convert_cli(
# fmt: off
input_path: str = Arg(..., help="Input file or directory", exists=True),
output_dir: Path = Arg("-", help="Output directory. '-' for stdout.", allow_dash=True, exists=True),
file_type: FileTypes = Opt("spacy", "--file-type", "-t", help="Type of data to produce"),
n_sents: int = Opt(1, "--n-sents", "-n", help="Number of sentences per doc (0 to disable)"),
seg_sents: bool = Opt(False, "--seg-sents", "-s", help="Segment sentences (for -c ner)"),
model: Optional[str] = Opt(None, "--model", "--base", "-b", help="Trained spaCy pipeline for sentence segmentation to use as base (for --seg-sents)"),
morphology: bool = Opt(False, "--morphology", "-m", help="Enable appending morphology to tags"),
merge_subtokens: bool = Opt(False, "--merge-subtokens", "-T", help="Merge CoNLL-U subtokens"),
converter: str = Opt(AUTO, "--converter", "-c", help=f"Converter: {tuple(CONVERTERS.keys())}"),
ner_map: Optional[Path] = Opt(None, "--ner-map", "-nm", help="NER tag mapping (as JSON-encoded dict of entity types)", exists=True),
lang: Optional[str] = Opt(None, "--lang", "-l", help="Language (if tokenizer required)"),
concatenate: bool = Opt(None, "--concatenate", "-C", help="Concatenate output to a single file"),
output_dir: Path = Arg(
"-", help="Output directory. '-' for stdout.", allow_dash=True, exists=True
),
file_type: FileTypes = Opt(
"spacy", "--file-type", "-t", help="Type of data to produce"
),
n_sents: int = Opt(
1, "--n-sents", "-n", help="Number of sentences per doc (0 to disable)"
),
seg_sents: bool = Opt(
False, "--seg-sents", "-s", help="Segment sentences (for -c ner)"
),
model: Optional[str] = Opt(
None,
"--model",
"--base",
"-b",
help="Trained spaCy pipeline for sentence segmentation to use as base (for --seg-sents)",
),
morphology: bool = Opt(
False, "--morphology", "-m", help="Enable appending morphology to tags"
),
merge_subtokens: bool = Opt(
False, "--merge-subtokens", "-T", help="Merge CoNLL-U subtokens"
),
converter: str = Opt(
AUTO, "--converter", "-c", help=f"Converter: {tuple(CONVERTERS.keys())}"
),
ner_map: Optional[Path] = Opt(
None,
"--ner-map",
"-nm",
help="NER tag mapping (as JSON-encoded dict of entity types)",
exists=True,
),
lang: Optional[str] = Opt(
None, "--lang", "-l", help="Language (if tokenizer required)"
),
concatenate: bool = Opt(
None, "--concatenate", "-C", help="Concatenate output to a single file"
),
# fmt: on
):
"""
+24 -6
View File
@@ -26,10 +26,28 @@ from ._util import (
def debug_config_cli(
# fmt: off
ctx: typer.Context, # This is only used to read additional arguments
config_path: Path = Arg(..., help="Path to config file", exists=True, allow_dash=True),
code_path: Optional[Path] = Opt(None, "--code-path", "--code", "-c", help="Path to Python file with additional code (registered functions) to be imported"),
show_funcs: bool = Opt(False, "--show-functions", "-F", help="Show an overview of all registered functions used in the config and where they come from (modules, files etc.)"),
show_vars: bool = Opt(False, "--show-variables", "-V", help="Show an overview of all variables referenced in the config and their values. This will also reflect variables overwritten on the CLI.")
config_path: Path = Arg(
..., help="Path to config file", exists=True, allow_dash=True
),
code_path: Optional[Path] = Opt(
None,
"--code-path",
"--code",
"-c",
help="Path to Python file with additional code (registered functions) to be imported",
),
show_funcs: bool = Opt(
False,
"--show-functions",
"-F",
help="Show an overview of all registered functions used in the config and where they come from (modules, files etc.)",
),
show_vars: bool = Opt(
False,
"--show-variables",
"-V",
help="Show an overview of all variables referenced in the config and their values. This will also reflect variables overwritten on the CLI.",
),
# fmt: on
):
"""Debug a config file and show validation errors. The command will
@@ -64,10 +82,10 @@ def debug_config(
config = nlp.config.interpolate()
msg.divider("Config validation for [initialize]")
with show_validation_error(config_path):
T = registry.resolve(config["initialize"], schema=ConfigSchemaInit)
T = registry.resolve(config["initialize"], schema=ConfigSchemaInit) # type: ignore[arg-type]
msg.divider("Config validation for [training]")
with show_validation_error(config_path):
T = registry.resolve(config["training"], schema=ConfigSchemaTraining)
T = registry.resolve(config["training"], schema=ConfigSchemaTraining) # type: ignore[arg-type]
dot_names = [T["train_corpus"], T["dev_corpus"]]
util.resolve_dot_names(config, dot_names)
msg.good("Config is valid")
+24 -7
View File
@@ -71,11 +71,28 @@ SPAN_LENGTH_THRESHOLD_PERCENTAGE = 90
def debug_data_cli(
# fmt: off
ctx: typer.Context, # This is only used to read additional arguments
config_path: Path = Arg(..., help="Path to config file", exists=True, allow_dash=True),
code_path: Optional[Path] = Opt(None, "--code-path", "--code", "-c", help="Path to Python file with additional code (registered functions) to be imported"),
ignore_warnings: bool = Opt(False, "--ignore-warnings", "-IW", help="Ignore warnings, only show stats and errors"),
verbose: bool = Opt(False, "--verbose", "-V", help="Print additional information and explanations"),
no_format: bool = Opt(False, "--no-format", "-NF", help="Don't pretty-print the results"),
config_path: Path = Arg(
..., help="Path to config file", exists=True, allow_dash=True
),
code_path: Optional[Path] = Opt(
None,
"--code-path",
"--code",
"-c",
help="Path to Python file with additional code (registered functions) to be imported",
),
ignore_warnings: bool = Opt(
False,
"--ignore-warnings",
"-IW",
help="Ignore warnings, only show stats and errors",
),
verbose: bool = Opt(
False, "--verbose", "-V", help="Print additional information and explanations"
),
no_format: bool = Opt(
False, "--no-format", "-NF", help="Don't pretty-print the results"
),
# fmt: on
):
"""
@@ -120,7 +137,7 @@ def debug_data(
cfg = util.load_config(config_path, overrides=config_overrides)
nlp = util.load_model_from_config(cfg)
config = nlp.config.interpolate()
T = registry.resolve(config["training"], schema=ConfigSchemaTraining)
T = registry.resolve(config["training"], schema=ConfigSchemaTraining) # type: ignore[arg-type]
# Use original config here, not resolved version
sourced_components = get_sourced_components(cfg)
frozen_components = T["frozen_components"]
@@ -708,7 +725,7 @@ def debug_data(
if len(dev_not_train) != 0:
pct = len(dev_not_train) / len(trees_dev)
msg.info(
f"{len(dev_not_train)} lemmatizer trees ({pct*100:.1f}% of dev trees)"
f"{len(dev_not_train)} lemmatizer trees ({pct * 100:.1f}% of dev trees)"
" were found exclusively in the dev data."
)
else:
+31 -8
View File
@@ -2,11 +2,10 @@ from pathlib import Path
from typing import Optional
import typer
from thinc.api import Config
from wasabi import MarkdownRenderer, Printer, diff_strings
from ..util import load_config
from ._util import Arg, Opt, debug_cli, parse_config_overrides, show_validation_error
from ._util import Arg, Opt, debug_cli, show_validation_error
from .init_config import Optimizations, init_config
@@ -17,12 +16,36 @@ from .init_config import Optimizations, init_config
def debug_diff_cli(
# fmt: off
ctx: typer.Context,
config_path: Path = Arg(..., help="Path to config file", exists=True, allow_dash=True),
compare_to: Optional[Path] = Opt(None, help="Path to a config file to diff against, or `None` to compare against default settings", exists=True, allow_dash=True),
optimize: Optimizations = Opt(Optimizations.efficiency.value, "--optimize", "-o", help="Whether the user config was optimized for efficiency or accuracy. Only relevant when comparing against the default config."),
gpu: bool = Opt(False, "--gpu", "-G", help="Whether the original config can run on a GPU. Only relevant when comparing against the default config."),
pretraining: bool = Opt(False, "--pretraining", "--pt", help="Whether to compare on a config with pretraining involved. Only relevant when comparing against the default config."),
markdown: bool = Opt(False, "--markdown", "-md", help="Generate Markdown for GitHub issues")
config_path: Path = Arg(
..., help="Path to config file", exists=True, allow_dash=True
),
compare_to: Optional[Path] = Opt(
None,
help="Path to a config file to diff against, or `None` to compare against default settings",
exists=True,
allow_dash=True,
),
optimize: Optimizations = Opt(
Optimizations.efficiency.value,
"--optimize",
"-o",
help="Whether the user config was optimized for efficiency or accuracy. Only relevant when comparing against the default config.",
),
gpu: bool = Opt(
False,
"--gpu",
"-G",
help="Whether the original config can run on a GPU. Only relevant when comparing against the default config.",
),
pretraining: bool = Opt(
False,
"--pretraining",
"--pt",
help="Whether to compare on a config with pretraining involved. Only relevant when comparing against the default config.",
),
markdown: bool = Opt(
False, "--markdown", "-md", help="Generate Markdown for GitHub issues"
),
# fmt: on
):
"""Show a diff of a config file with respect to spaCy's defaults or another config file. If
+14 -6
View File
@@ -36,18 +36,26 @@ from ._util import (
def debug_model_cli(
# fmt: off
ctx: typer.Context, # This is only used to read additional arguments
config_path: Path = Arg(..., help="Path to config file", exists=True, allow_dash=True),
component: str = Arg(..., help="Name of the pipeline component of which the model should be analysed"),
layers: str = Opt("", "--layers", "-l", help="Comma-separated names of layer IDs to print"),
config_path: Path = Arg(
..., help="Path to config file", exists=True, allow_dash=True
),
component: str = Arg(
..., help="Name of the pipeline component of which the model should be analysed"
),
layers: str = Opt(
"", "--layers", "-l", help="Comma-separated names of layer IDs to print"
),
dimensions: bool = Opt(False, "--dimensions", "-DIM", help="Show dimensions"),
parameters: bool = Opt(False, "--parameters", "-PAR", help="Show parameters"),
gradients: bool = Opt(False, "--gradients", "-GRAD", help="Show gradients"),
attributes: bool = Opt(False, "--attributes", "-ATTR", help="Show attributes"),
P0: bool = Opt(False, "--print-step0", "-P0", help="Print model before training"),
P1: bool = Opt(False, "--print-step1", "-P1", help="Print model after initialization"),
P1: bool = Opt(
False, "--print-step1", "-P1", help="Print model after initialization"
),
P2: bool = Opt(False, "--print-step2", "-P2", help="Print model after training"),
P3: bool = Opt(False, "--print-step3", "-P3", help="Print final predictions"),
use_gpu: int = Opt(-1, "--gpu-id", "-g", help="GPU ID or -1 for CPU")
use_gpu: int = Opt(-1, "--gpu-id", "-g", help="GPU ID or -1 for CPU"),
# fmt: on
):
"""
@@ -81,7 +89,7 @@ def debug_model_cli(
with show_validation_error(config_path):
nlp = util.load_model_from_config(raw_config)
config = nlp.config.interpolate()
T = registry.resolve(config["training"], schema=ConfigSchemaTraining)
T = registry.resolve(config["training"], schema=ConfigSchemaTraining) # type: ignore[arg-type]
seed = T["seed"]
if seed is not None:
msg.info(f"Fixing random seed: {seed}")
+26 -4
View File
@@ -1,3 +1,4 @@
import shutil
import sys
from typing import Optional, Sequence
from urllib.parse import urljoin
@@ -27,9 +28,16 @@ def download_cli(
# fmt: off
ctx: typer.Context,
model: str = Arg(..., help="Name of pipeline package to download"),
direct: bool = Opt(False, "--direct", "-d", "-D", help="Force direct download of name + version"),
sdist: bool = Opt(False, "--sdist", "-S", help="Download sdist (.tar.gz) archive instead of pre-built binary wheel"),
url: str = Opt(None, "--url", "-U", help="Download from given url")
direct: bool = Opt(
False, "--direct", "-d", "-D", help="Force direct download of name + version"
),
sdist: bool = Opt(
False,
"--sdist",
"-S",
help="Download sdist (.tar.gz) archive instead of pre-built binary wheel",
),
url: str = Opt(None, "--url", "-U", help="Download from given url"),
# fmt: on
):
"""
@@ -176,5 +184,19 @@ def download_model(
if not download_url.startswith(about.__download_url__):
raise ValueError(f"Download from {filename} rejected. Was it a relative path?")
pip_args = list(user_pip_args) if user_pip_args is not None else []
cmd = [sys.executable, "-m", "pip", "install"] + pip_args + [download_url]
cmd = _get_pip_install_cmd() + pip_args + [download_url]
run_command(cmd)
def _get_pip_install_cmd() -> list:
if shutil.which("pip"):
return [sys.executable, "-m", "pip", "install"]
elif shutil.which("uv"):
return ["uv", "pip", "install"]
else:
msg.fail(
"No package installer found",
"spaCy requires either pip or uv to download models. "
"Please install one of them and try again.",
exits=1,
)
+37 -11
View File
@@ -1,13 +1,12 @@
import re
from pathlib import Path
from typing import Any, Dict, List, Optional, Union
from typing import Any, Dict, List, Optional
import srsly
from thinc.api import fix_random_seed
from wasabi import Printer
from .. import displacy, util
from ..scorer import Scorer
from ..tokens import Doc
from ..training import Corpus
from ._util import Arg, Opt, app, benchmark_cli, import_code, setup_gpu
@@ -20,15 +19,42 @@ from ._util import Arg, Opt, app, benchmark_cli, import_code, setup_gpu
def evaluate_cli(
# fmt: off
model: str = Arg(..., help="Model name or path"),
data_path: Path = Arg(..., help="Location of binary evaluation data in .spacy format", exists=True),
output: Optional[Path] = Opt(None, "--output", "-o", help="Output JSON file for metrics", dir_okay=False),
code_path: Optional[Path] = Opt(None, "--code", "-c", help="Path to Python file with additional code (registered functions) to be imported"),
data_path: Path = Arg(
..., help="Location of binary evaluation data in .spacy format", exists=True
),
output: Optional[Path] = Opt(
None, "--output", "-o", help="Output JSON file for metrics", dir_okay=False
),
code_path: Optional[Path] = Opt(
None,
"--code",
"-c",
help="Path to Python file with additional code (registered functions) to be imported",
),
use_gpu: int = Opt(-1, "--gpu-id", "-g", help="GPU ID or -1 for CPU"),
gold_preproc: bool = Opt(False, "--gold-preproc", "-G", help="Use gold preprocessing"),
displacy_path: Optional[Path] = Opt(None, "--displacy-path", "-dp", help="Directory to output rendered parses as HTML", exists=True, file_okay=False),
displacy_limit: int = Opt(25, "--displacy-limit", "-dl", help="Limit of parses to render as HTML"),
per_component: bool = Opt(False, "--per-component", "-P", help="Return scores per component, only applicable when an output JSON file is specified."),
spans_key: str = Opt("sc", "--spans-key", "-sk", help="Spans key to use when evaluating Doc.spans"),
gold_preproc: bool = Opt(
False, "--gold-preproc", "-G", help="Use gold preprocessing"
),
displacy_path: Optional[Path] = Opt(
None,
"--displacy-path",
"-dp",
help="Directory to output rendered parses as HTML",
exists=True,
file_okay=False,
),
displacy_limit: int = Opt(
25, "--displacy-limit", "-dl", help="Limit of parses to render as HTML"
),
per_component: bool = Opt(
False,
"--per-component",
"-P",
help="Return scores per component, only applicable when an output JSON file is specified.",
),
spans_key: str = Opt(
"sc", "--spans-key", "-sk", help="Spans key to use when evaluating Doc.spans"
),
# fmt: on
):
"""
@@ -123,7 +149,7 @@ def evaluate(
if key == "speed":
results[metric] = f"{scores[key]:.0f}"
else:
results[metric] = f"{scores[key]*100:.2f}"
results[metric] = f"{scores[key] * 100:.2f}"
else:
results[metric] = "-"
data[re.sub(r"[\s/]", "_", key.lower())] = scores[key]
+3 -1
View File
@@ -11,7 +11,9 @@ from ._util import Arg, Opt, app
def find_function_cli(
# fmt: off
func_name: str = Arg(..., help="Name of the registered function."),
registry_name: Optional[str] = Opt(None, "--registry", "-r", help="Name of the catalogue registry."),
registry_name: Optional[str] = Opt(
None, "--registry", "-r", help="Name of the catalogue registry."
),
# fmt: on
):
"""
+35 -11
View File
@@ -27,15 +27,39 @@ _DEFAULTS = {
def find_threshold_cli(
# fmt: off
model: str = Arg(..., help="Model name or path"),
data_path: Path = Arg(..., help="Location of binary evaluation data in .spacy format", exists=True),
data_path: Path = Arg(
..., help="Location of binary evaluation data in .spacy format", exists=True
),
pipe_name: str = Arg(..., help="Name of pipe to examine thresholds for"),
threshold_key: str = Arg(..., help="Key of threshold attribute in component's configuration"),
threshold_key: str = Arg(
..., help="Key of threshold attribute in component's configuration"
),
scores_key: str = Arg(..., help="Metric to optimize"),
n_trials: int = Opt(_DEFAULTS["n_trials"], "--n_trials", "-n", help="Number of trials to determine optimal thresholds"),
code_path: Optional[Path] = Opt(None, "--code", "-c", help="Path to Python file with additional code (registered functions) to be imported"),
use_gpu: int = Opt(_DEFAULTS["use_gpu"], "--gpu-id", "-g", help="GPU ID or -1 for CPU"),
gold_preproc: bool = Opt(_DEFAULTS["gold_preproc"], "--gold-preproc", "-G", help="Use gold preprocessing"),
verbose: bool = Opt(False, "--verbose", "-V", "-VV", help="Display more information for debugging purposes"),
n_trials: int = Opt(
_DEFAULTS["n_trials"],
"--n_trials",
"-n",
help="Number of trials to determine optimal thresholds",
),
code_path: Optional[Path] = Opt(
None,
"--code",
"-c",
help="Path to Python file with additional code (registered functions) to be imported",
),
use_gpu: int = Opt(
_DEFAULTS["use_gpu"], "--gpu-id", "-g", help="GPU ID or -1 for CPU"
),
gold_preproc: bool = Opt(
_DEFAULTS["gold_preproc"], "--gold-preproc", "-G", help="Use gold preprocessing"
),
verbose: bool = Opt(
False,
"--verbose",
"-V",
"-VV",
help="Display more information for debugging purposes",
),
# fmt: on
):
"""
@@ -183,10 +207,10 @@ def find_threshold(
),
)
if hasattr(pipe, "cfg"):
setattr(
nlp.get_pipe(pipe_name),
"cfg",
set_nested_item(getattr(pipe, "cfg"), config_keys, threshold),
nlp.get_pipe(pipe_name).cfg = set_nested_item( # type: ignore[attr-defined]
pipe.cfg,
config_keys,
threshold, # type: ignore[attr-defined]
)
eval_scores = nlp.evaluate(dev_dataset)
+18 -4
View File
@@ -16,10 +16,24 @@ from .download import get_latest_version, get_model_filename
def info_cli(
# fmt: off
model: Optional[str] = Arg(None, help="Optional loadable spaCy pipeline"),
markdown: bool = Opt(False, "--markdown", "-md", help="Generate Markdown for GitHub issues"),
silent: bool = Opt(False, "--silent", "-s", "-S", help="Don't print anything (just return)"),
exclude: str = Opt("labels", "--exclude", "-e", help="Comma-separated keys to exclude from the print-out"),
url: bool = Opt(False, "--url", "-u", help="Print the URL to download the most recent compatible version of the pipeline"),
markdown: bool = Opt(
False, "--markdown", "-md", help="Generate Markdown for GitHub issues"
),
silent: bool = Opt(
False, "--silent", "-s", "-S", help="Don't print anything (just return)"
),
exclude: str = Opt(
"labels",
"--exclude",
"-e",
help="Comma-separated keys to exclude from the print-out",
),
url: bool = Opt(
False,
"--url",
"-u",
help="Print the URL to download the most recent compatible version of the pipeline",
),
# fmt: on
):
"""
+61 -13
View File
@@ -49,13 +49,44 @@ class InitValues:
@init_cli.command("config")
def init_config_cli(
# fmt: off
output_file: Path = Arg(..., help="File to save the config to or - for stdout (will only output config and no additional logging info)", allow_dash=True),
lang: str = Opt(InitValues.lang, "--lang", "-l", help="Two-letter code of the language to use"),
pipeline: str = Opt(",".join(InitValues.pipeline), "--pipeline", "-p", help="Comma-separated names of trainable pipeline components to include (without 'tok2vec' or 'transformer')"),
optimize: Optimizations = Opt(InitValues.optimize, "--optimize", "-o", help="Whether to optimize for efficiency (faster inference, smaller model, lower memory consumption) or higher accuracy (potentially larger and slower model). This will impact the choice of architecture, pretrained weights and related hyperparameters."),
gpu: bool = Opt(InitValues.gpu, "--gpu", "-G", help="Whether the model can run on GPU. This will impact the choice of architecture, pretrained weights and related hyperparameters."),
pretraining: bool = Opt(InitValues.pretraining, "--pretraining", "-pt", help="Include config for pretraining (with 'spacy pretrain')"),
force_overwrite: bool = Opt(InitValues.force_overwrite, "--force", "-F", help="Force overwriting the output file"),
output_file: Path = Arg(
...,
help="File to save the config to or - for stdout (will only output config and no additional logging info)",
allow_dash=True,
),
lang: str = Opt(
InitValues.lang, "--lang", "-l", help="Two-letter code of the language to use"
),
pipeline: str = Opt(
",".join(InitValues.pipeline),
"--pipeline",
"-p",
help="Comma-separated names of trainable pipeline components to include (without 'tok2vec' or 'transformer')",
),
optimize: Optimizations = Opt(
InitValues.optimize,
"--optimize",
"-o",
help="Whether to optimize for efficiency (faster inference, smaller model, lower memory consumption) or higher accuracy (potentially larger and slower model). This will impact the choice of architecture, pretrained weights and related hyperparameters.",
),
gpu: bool = Opt(
InitValues.gpu,
"--gpu",
"-G",
help="Whether the model can run on GPU. This will impact the choice of architecture, pretrained weights and related hyperparameters.",
),
pretraining: bool = Opt(
InitValues.pretraining,
"--pretraining",
"-pt",
help="Include config for pretraining (with 'spacy pretrain')",
),
force_overwrite: bool = Opt(
InitValues.force_overwrite,
"--force",
"-F",
help="Force overwriting the output file",
),
# fmt: on
):
"""
@@ -88,11 +119,28 @@ def init_config_cli(
@init_cli.command("fill-config")
def init_fill_config_cli(
# fmt: off
base_path: Path = Arg(..., help="Path to base config to fill", exists=True, dir_okay=False),
output_file: Path = Arg("-", help="Path to output .cfg file (or - for stdout)", allow_dash=True),
pretraining: bool = Opt(False, "--pretraining", "-pt", help="Include config for pretraining (with 'spacy pretrain')"),
diff: bool = Opt(False, "--diff", "-D", help="Print a visual diff highlighting the changes"),
code_path: Optional[Path] = Opt(None, "--code-path", "--code", "-c", help="Path to Python file with additional code (registered functions) to be imported"),
base_path: Path = Arg(
..., help="Path to base config to fill", exists=True, dir_okay=False
),
output_file: Path = Arg(
"-", help="Path to output .cfg file (or - for stdout)", allow_dash=True
),
pretraining: bool = Opt(
False,
"--pretraining",
"-pt",
help="Include config for pretraining (with 'spacy pretrain')",
),
diff: bool = Opt(
False, "--diff", "-D", help="Print a visual diff highlighting the changes"
),
code_path: Optional[Path] = Opt(
None,
"--code-path",
"--code",
"-c",
help="Path to Python file with additional code (registered functions) to be imported",
),
# fmt: on
):
"""
@@ -168,7 +216,7 @@ def init_config(
# Filter out duplicates since tok2vec and transformer are added by template
pipeline = [pipe for pipe in pipeline if pipe not in ("tok2vec", "transformer")]
defaults = RECOMMENDATIONS["__default__"]
reco = RecommendationSchema(**RECOMMENDATIONS.get(lang, defaults)).dict()
reco = RecommendationSchema(**RECOMMENDATIONS.get(lang, defaults)).model_dump()
variables = {
"lang": lang,
"components": pipeline,
+69 -14
View File
@@ -26,13 +26,42 @@ def init_vectors_cli(
lang: str = Arg(..., help="The language of the nlp object to create"),
vectors_loc: Path = Arg(..., help="Vectors file in Word2Vec format", exists=True),
output_dir: Path = Arg(..., help="Pipeline output directory"),
prune: int = Opt(-1, "--prune", "-p", help="Optional number of vectors to prune to"),
truncate: int = Opt(0, "--truncate", "-t", help="Optional number of vectors to truncate to when reading in vectors file"),
prune: int = Opt(
-1, "--prune", "-p", help="Optional number of vectors to prune to"
),
truncate: int = Opt(
0,
"--truncate",
"-t",
help="Optional number of vectors to truncate to when reading in vectors file",
),
mode: str = Opt("default", "--mode", "-m", help="Vectors mode: default or floret"),
name: Optional[str] = Opt(None, "--name", "-n", help="Optional name for the word vectors, e.g. en_core_web_lg.vectors"),
verbose: bool = Opt(False, "--verbose", "-V", "-VV", help="Display more information for debugging purposes"),
jsonl_loc: Optional[Path] = Opt(None, "--lexemes-jsonl", "-j", help="Location of JSONL-formatted attributes file", hidden=True),
attr: str = Opt("ORTH", "--attr", "-a", help="Optional token attribute to use for vectors, e.g. LOWER or NORM"),
name: Optional[str] = Opt(
None,
"--name",
"-n",
help="Optional name for the word vectors, e.g. en_core_web_lg.vectors",
),
verbose: bool = Opt(
False,
"--verbose",
"-V",
"-VV",
help="Display more information for debugging purposes",
),
jsonl_loc: Optional[Path] = Opt(
None,
"--lexemes-jsonl",
"-j",
help="Location of JSONL-formatted attributes file",
hidden=True,
),
attr: str = Opt(
"ORTH",
"--attr",
"-a",
help="Optional token attribute to use for vectors, e.g. LOWER or NORM",
),
# fmt: on
):
"""Convert word vectors for use with spaCy. Will export an nlp object that
@@ -81,11 +110,24 @@ def update_lexemes(nlp: Language, jsonl_loc: Path) -> None:
def init_pipeline_cli(
# fmt: off
ctx: typer.Context, # This is only used to read additional arguments
config_path: Path = Arg(..., help="Path to config file", exists=True, allow_dash=True),
config_path: Path = Arg(
..., help="Path to config file", exists=True, allow_dash=True
),
output_path: Path = Arg(..., help="Output directory for the prepared data"),
code_path: Optional[Path] = Opt(None, "--code", "-c", help="Path to Python file with additional code (registered functions) to be imported"),
verbose: bool = Opt(False, "--verbose", "-V", "-VV", help="Display more information for debugging purposes"),
use_gpu: int = Opt(-1, "--gpu-id", "-g", help="GPU ID or -1 for CPU")
code_path: Optional[Path] = Opt(
None,
"--code",
"-c",
help="Path to Python file with additional code (registered functions) to be imported",
),
verbose: bool = Opt(
False,
"--verbose",
"-V",
"-VV",
help="Display more information for debugging purposes",
),
use_gpu: int = Opt(-1, "--gpu-id", "-g", help="GPU ID or -1 for CPU"),
# fmt: on
):
if verbose:
@@ -108,11 +150,24 @@ def init_pipeline_cli(
def init_labels_cli(
# fmt: off
ctx: typer.Context, # This is only used to read additional arguments
config_path: Path = Arg(..., help="Path to config file", exists=True, allow_dash=True),
config_path: Path = Arg(
..., help="Path to config file", exists=True, allow_dash=True
),
output_path: Path = Arg(..., help="Output directory for the labels"),
code_path: Optional[Path] = Opt(None, "--code", "-c", help="Path to Python file with additional code (registered functions) to be imported"),
verbose: bool = Opt(False, "--verbose", "-V", "-VV", help="Display more information for debugging purposes"),
use_gpu: int = Opt(-1, "--gpu-id", "-g", help="GPU ID or -1 for CPU")
code_path: Optional[Path] = Opt(
None,
"--code",
"-c",
help="Path to Python file with additional code (registered functions) to be imported",
),
verbose: bool = Opt(
False,
"--verbose",
"-V",
"-VV",
help="Display more information for debugging purposes",
),
use_gpu: int = Opt(-1, "--gpu-id", "-g", help="GPU ID or -1 for CPU"),
# fmt: on
):
"""Generate JSON files for the labels in the data. This helps speed up the
+53 -15
View File
@@ -21,16 +21,56 @@ from ._util import SDIST_SUFFIX, WHEEL_SUFFIX, Arg, Opt, app, string_to_list
@app.command("package")
def package_cli(
# fmt: off
input_dir: Path = Arg(..., help="Directory with pipeline data", exists=True, file_okay=False),
output_dir: Path = Arg(..., help="Output parent directory", exists=True, file_okay=False),
code_paths: str = Opt("", "--code", "-c", help="Comma-separated paths to Python file with additional code (registered functions) to be included in the package"),
meta_path: Optional[Path] = Opt(None, "--meta-path", "--meta", "-m", help="Path to meta.json", exists=True, dir_okay=False),
create_meta: bool = Opt(False, "--create-meta", "-C", help="Create meta.json, even if one exists"),
name: Optional[str] = Opt(None, "--name", "-n", help="Package name to override meta"),
version: Optional[str] = Opt(None, "--version", "-v", help="Package version to override meta"),
build: str = Opt("sdist", "--build", "-b", help="Comma-separated formats to build: sdist and/or wheel, or none."),
force: bool = Opt(False, "--force", "-f", "-F", help="Force overwriting existing data in output directory"),
require_parent: bool = Opt(True, "--require-parent/--no-require-parent", "-R", "-R", help="Include the parent package (e.g. spacy) in the requirements"),
input_dir: Path = Arg(
..., help="Directory with pipeline data", exists=True, file_okay=False
),
output_dir: Path = Arg(
..., help="Output parent directory", exists=True, file_okay=False
),
code_paths: str = Opt(
"",
"--code",
"-c",
help="Comma-separated paths to Python file with additional code (registered functions) to be included in the package",
),
meta_path: Optional[Path] = Opt(
None,
"--meta-path",
"--meta",
"-m",
help="Path to meta.json",
exists=True,
dir_okay=False,
),
create_meta: bool = Opt(
False, "--create-meta", "-C", help="Create meta.json, even if one exists"
),
name: Optional[str] = Opt(
None, "--name", "-n", help="Package name to override meta"
),
version: Optional[str] = Opt(
None, "--version", "-v", help="Package version to override meta"
),
build: str = Opt(
"sdist",
"--build",
"-b",
help="Comma-separated formats to build: sdist and/or wheel, or none.",
),
force: bool = Opt(
False,
"--force",
"-f",
"-F",
help="Force overwriting existing data in output directory",
),
require_parent: bool = Opt(
True,
"--require-parent/--no-require-parent",
"-R",
"-R",
help="Include the parent package (e.g. spacy) in the requirements",
),
# fmt: on
):
"""
@@ -410,7 +450,7 @@ def generate_readme(meta: Dict[str, Any]) -> str:
pipeline = ", ".join([md.code(p) for p in meta.get("pipeline", [])])
components = ", ".join([md.code(p) for p in meta.get("components", [])])
vecs = meta.get("vectors", {})
vectors = f"{vecs.get('keys', 0)} keys, {vecs.get('vectors', 0)} unique vectors ({ vecs.get('width', 0)} dimensions)"
vectors = f"{vecs.get('keys', 0)} keys, {vecs.get('vectors', 0)} unique vectors ({vecs.get('width', 0)} dimensions)"
author = meta.get("author") or "n/a"
notes = meta.get("notes", "")
license_name = meta.get("license")
@@ -469,7 +509,7 @@ def _format_accuracy(data: Dict[str, Any], exclude: List[str] = ["speed"]) -> st
md = MarkdownRenderer()
scalars = [(k, v) for k, v in data.items() if isinstance(v, (int, float))]
scores = [
(md.code(acc.upper()), f"{score*100:.2f}")
(md.code(acc.upper()), f"{score * 100:.2f}")
for acc, score in scalars
if acc not in exclude
]
@@ -488,9 +528,7 @@ def _format_label_scheme(data: Dict[str, Any]) -> str:
if not labels:
continue
col1 = md.bold(md.code(pipe))
col2 = ", ".join(
[md.code(str(label).replace("|", "\\|")) for label in labels]
) # noqa: W605
col2 = ", ".join([md.code(str(label).replace("|", "\\|")) for label in labels]) # noqa: W605
label_data.append((col1, col2))
n_labels += len(labels)
n_pipes += 1
+24 -5
View File
@@ -25,13 +25,32 @@ from ._util import (
def pretrain_cli(
# fmt: off
ctx: typer.Context, # This is only used to read additional arguments
config_path: Path = Arg(..., help="Path to config file", exists=True, dir_okay=False, allow_dash=True),
config_path: Path = Arg(
..., help="Path to config file", exists=True, dir_okay=False, allow_dash=True
),
output_dir: Path = Arg(..., help="Directory to write weights to on each epoch"),
code_path: Optional[Path] = Opt(None, "--code", "-c", help="Path to Python file with additional code (registered functions) to be imported"),
resume_path: Optional[Path] = Opt(None, "--resume-path", "-r", help="Path to pretrained weights from which to resume pretraining"),
epoch_resume: Optional[int] = Opt(None, "--epoch-resume", "-er", help="The epoch to resume counting from when using --resume-path. Prevents unintended overwriting of existing weight files."),
code_path: Optional[Path] = Opt(
None,
"--code",
"-c",
help="Path to Python file with additional code (registered functions) to be imported",
),
resume_path: Optional[Path] = Opt(
None,
"--resume-path",
"-r",
help="Path to pretrained weights from which to resume pretraining",
),
epoch_resume: Optional[int] = Opt(
None,
"--epoch-resume",
"-er",
help="The epoch to resume counting from when using --resume-path. Prevents unintended overwriting of existing weight files.",
),
use_gpu: int = Opt(-1, "--gpu-id", "-g", help="GPU ID or -1 for CPU"),
skip_last: bool = Opt(False, "--skip-last", "-L", help="Skip saving model-last.bin"),
skip_last: bool = Opt(
False, "--skip-last", "-L", help="Skip saving model-last.bin"
),
# fmt: on
):
"""
+9 -2
View File
@@ -21,8 +21,15 @@ def profile_cli(
# fmt: off
ctx: typer.Context, # This is only used to read current calling context
model: str = Arg(..., help="Trained pipeline to load"),
inputs: Optional[Path] = Arg(None, help="Location of input file. '-' for stdin.", exists=True, allow_dash=True),
n_texts: int = Opt(10000, "--n-texts", "-n", help="Maximum number of texts to use if available"),
inputs: Optional[Path] = Arg(
None,
help="Location of input file. '-' for stdin.",
exists=True,
allow_dash=True,
),
n_texts: int = Opt(
10000, "--n-texts", "-n", help="Maximum number of texts to use if available"
),
# fmt: on
):
"""
+24 -5
View File
@@ -26,11 +26,30 @@ from ._util import (
def train_cli(
# fmt: off
ctx: typer.Context, # This is only used to read additional arguments
config_path: Path = Arg(..., help="Path to config file", exists=True, allow_dash=True),
output_path: Optional[Path] = Opt(None, "--output", "--output-path", "-o", help="Output directory to store trained pipeline in"),
code_path: Optional[Path] = Opt(None, "--code", "-c", help="Path to Python file with additional code (registered functions) to be imported"),
verbose: bool = Opt(False, "--verbose", "-V", "-VV", help="Display more information for debugging purposes"),
use_gpu: int = Opt(-1, "--gpu-id", "-g", help="GPU ID or -1 for CPU")
config_path: Path = Arg(
..., help="Path to config file", exists=True, allow_dash=True
),
output_path: Optional[Path] = Opt(
None,
"--output",
"--output-path",
"-o",
help="Output directory to store trained pipeline in",
),
code_path: Optional[Path] = Opt(
None,
"--code",
"-c",
help="Path to Python file with additional code (registered functions) to be imported",
),
verbose: bool = Opt(
False,
"--verbose",
"-V",
"-VV",
help="Display more information for debugging purposes",
),
use_gpu: int = Opt(-1, "--gpu-id", "-g", help="GPU ID or -1 for CPU"),
# fmt: on
):
"""
+3 -1
View File
@@ -35,7 +35,9 @@ else:
try: # Python 3.8+
import importlib.metadata as importlib_metadata
except ImportError:
from catalogue import _importlib_metadata as importlib_metadata # type: ignore[no-redef] # noqa: F401
from catalogue import ( # type: ignore[no-redef]
_importlib_metadata as importlib_metadata, # noqa: F401
)
from thinc.api import Optimizer # noqa: F401
+1 -1
View File
@@ -60,7 +60,7 @@ _ordinal_words = [
"አስራ ስምንተኛ",
"አስራ ዘጠነኛ",
"ሃያኛ",
"ሰላሳኛ" "አርባኛ",
"ሰላሳኛአርባኛ",
"አምሳኛ",
"ስድሳኛ",
"ሰባኛ",
-1
View File
@@ -128,7 +128,6 @@ _other_exc = {
_exc.update(_other_exc)
for h in range(1, 12 + 1):
for period in ["π.μ.", "πμ"]:
_exc[f"{h}{period}"] = [
{ORTH: f"{h}"},
+4 -1
View File
@@ -415,7 +415,10 @@ class SpanishLemmatizer(Lemmatizer):
else:
rule = self.select_rule("verb", features)
verb_lemma = self.lemmatize_verb(
verb, features - {"PronType=Prs"}, rule, index # type: ignore[operator]
verb,
features - {"PronType=Prs"}, # type: ignore[operator]
rule,
index, # type: ignore[operator]
)[0]
pron_lemmas = []
for pron in prons:
+3 -3
View File
@@ -611,8 +611,8 @@ narrative_ends = ["ه‌ام", "ه‌ای", "ه", "ه‌ایم", "ه‌اید",
present_ends = ["م", "ی", "د", "یم", "ید", "ند"]
# special case of '#هست':
VERBS_EXC.update({conj: "هست" for conj in ["هست" + end for end in simple_ends]})
VERBS_EXC.update({conj: "هست" for conj in ["نیست" + end for end in simple_ends]})
VERBS_EXC.update(dict.fromkeys(["هست" + end for end in simple_ends], "هست"))
VERBS_EXC.update(dict.fromkeys(["نیست" + end for end in simple_ends], "هست"))
for verb_root in verb_roots:
conjugations = []
@@ -648,4 +648,4 @@ for verb_root in verb_roots:
)
)
VERBS_EXC.update({conj: (past,) if past else present for conj in conjugations})
VERBS_EXC.update(dict.fromkeys(conjugations, (past,) if past else present))
+1 -1
View File
@@ -1,4 +1,4 @@
from typing import Dict, List, Tuple
from typing import List, Tuple
from ...pipeline import Lemmatizer
from ...tokens import Token
+1 -3
View File
@@ -382,7 +382,5 @@ urrainn
ì
ò
ó
""".split(
"\n"
)
""".split("\n")
)
+1 -3
View File
@@ -1974,9 +1974,7 @@ Tron an
tuilleadh 's a chòir
Tuilleadh 's a chòir
tuilleadh sa chòir
Tuilleadh sa chòir""".split(
"\n"
):
Tuilleadh sa chòir""".split("\n"):
_exc[orth] = [{ORTH: orth}]
-1
View File
@@ -1,6 +1,5 @@
from typing import List, Tuple
from ...lookups import Lookups
from ...pipeline import Lemmatizer
from ...tokens import Token
+2 -2
View File
@@ -156,7 +156,7 @@ for orth in [
"S.T.",
"S.T.Han",
"S.Th.",
"S.Th.I" "S.TI.",
"S.Th.IS.TI.",
"S.T.P.",
"S.TrK",
"S.Tekp.",
@@ -210,7 +210,7 @@ for orth in [
"hlm.",
"i/o",
"n.b.",
"p.p." "pjs.",
"p.p.pjs.",
"s.d.",
"tel.",
"u.p.",
+1 -1
View File
@@ -10,7 +10,7 @@ sentences = [
"Berapa banyak pelajar yang akan menghadiri majlis perpisahan sekolah?",
"Pengeluaran makanan berasal dari beberapa lokasi termasuk Cameron Highlands, Johor Bahru, dan Kuching.",
"Syarikat XYZ telah menghasilkan 20,000 unit produk baharu dalam setahun terakhir",
"Kuala Lumpur merupakan ibu negara Malaysia." "Kau berada di mana semalam?",
"Kuala Lumpur merupakan ibu negara Malaysia.Kau berada di mana semalam?",
"Siapa yang akan memimpin projek itu?",
"Siapa perdana menteri Malaysia sekarang?",
]
+5 -3
View File
@@ -1,6 +1,8 @@
from ..punctuation import TOKENIZER_INFIXES as BASE_TOKENIZER_INFIXES
from ..punctuation import TOKENIZER_PREFIXES as BASE_TOKENIZER_PREFIXES
from ..punctuation import TOKENIZER_SUFFIXES as BASE_TOKENIZER_SUFFIXES
from ..punctuation import (
TOKENIZER_INFIXES as BASE_TOKENIZER_INFIXES,
TOKENIZER_PREFIXES as BASE_TOKENIZER_PREFIXES,
TOKENIZER_SUFFIXES as BASE_TOKENIZER_SUFFIXES,
)
_prefixes = [r"\w{1,3}\$"] + BASE_TOKENIZER_PREFIXES
-2
View File
@@ -5,14 +5,12 @@ from ..char_classes import (
CONCAT_QUOTES,
CURRENCY,
HYPHENS,
LIST_CURRENCY,
LIST_ELLIPSES,
LIST_ICONS,
LIST_PUNCT,
LIST_QUOTES,
PUNCT,
UNITS,
merge_chars,
)
from ..punctuation import TOKENIZER_PREFIXES as BASE_TOKENIZER_PREFIXES
+5 -5
View File
@@ -34,11 +34,11 @@ URL_PATTERN = (
# host & domain names
# mods: match is case-sensitive, so include [A-Z]
r"(?:" # noqa: E131
r"(?:" # noqa: E131
r"[A-Za-z0-9\u00a1-\uffff]" # noqa: E131
r"[A-Za-z0-9\u00a1-\uffff_-]{0,62}"
r")?"
r"[A-Za-z0-9\u00a1-\uffff]\."
r"(?:" # noqa: E131
r"[A-Za-z0-9\u00a1-\uffff]" # noqa: E131
r"[A-Za-z0-9\u00a1-\uffff_-]{0,62}"
r")?"
r"[A-Za-z0-9\u00a1-\uffff]\."
r")+"
# TLD identifier
# mods: use ALPHA_LOWER instead of a wider range so that this doesn't match
+1 -3
View File
@@ -1943,7 +1943,5 @@ yêu_cầu
ừ_ào
ừ_ừ
""".split(
"\n"
)
""".split("\n")
)
+5 -5
View File
@@ -91,13 +91,13 @@ class ChineseTokenizer(DummyTokenizer):
def __call__(self, text: str) -> Doc:
if self.segmenter == Segmenter.jieba:
words = list([x for x in self.jieba_seg.cut(text, cut_all=False) if x]) # type: ignore[union-attr]
(words, spaces) = util.get_words_and_spaces(words, text)
words, spaces = util.get_words_and_spaces(words, text)
return Doc(self.vocab, words=words, spaces=spaces)
elif self.segmenter == Segmenter.pkuseg:
if self.pkuseg_seg is None:
raise ValueError(Errors.E1000)
words = self.pkuseg_seg.cut(text)
(words, spaces) = util.get_words_and_spaces(words, text)
words, spaces = util.get_words_and_spaces(words, text)
return Doc(self.vocab, words=words, spaces=spaces)
# warn if segmenter setting is not the only remaining option "char"
@@ -112,7 +112,7 @@ class ChineseTokenizer(DummyTokenizer):
# split into individual characters
words = list(text)
(words, spaces) = util.get_words_and_spaces(words, text)
words, spaces = util.get_words_and_spaces(words, text)
return Doc(self.vocab, words=words, spaces=spaces)
def pkuseg_update_user_dict(self, words: List[str], reset: bool = False):
@@ -210,7 +210,7 @@ class ChineseTokenizer(DummyTokenizer):
self.pkuseg_seg = spacy_pkuseg.pkuseg(str(tempdir))
if pkuseg_data["processors_data"]:
processors_data = pkuseg_data["processors_data"]
(user_dict, do_process, common_words, other_words) = processors_data
user_dict, do_process, common_words, other_words = processors_data
self.pkuseg_seg.preprocesser = spacy_pkuseg.Preprocesser(user_dict)
self.pkuseg_seg.postprocesser.do_process = do_process
self.pkuseg_seg.postprocesser.common_words = set(common_words)
@@ -268,7 +268,7 @@ class ChineseTokenizer(DummyTokenizer):
raise ImportError(self._pkuseg_install_msg) from None
if self.segmenter == Segmenter.pkuseg:
data = srsly.read_msgpack(path)
(user_dict, do_process, common_words, other_words) = data
user_dict, do_process, common_words, other_words = data
self.pkuseg_seg.preprocesser = spacy_pkuseg.Preprocesser(user_dict)
self.pkuseg_seg.postprocesser.do_process = do_process
self.pkuseg_seg.postprocesser.common_words = set(common_words)
+12 -8
View File
@@ -1323,7 +1323,7 @@ class Language:
# Make sure the config is interpolated so we can resolve subsections
config = self.config.interpolate()
# These are the settings provided in the [initialize] block in the config
I = registry.resolve(config["initialize"], schema=ConfigSchemaInit)
I = registry.resolve(config["initialize"], schema=ConfigSchemaInit) # type: ignore[arg-type]
before_init = I["before_init"]
if before_init is not None:
before_init(self)
@@ -1353,7 +1353,7 @@ class Language:
proc.initialize(get_examples, nlp=self, **p_settings)
pretrain_cfg = config.get("pretraining")
if pretrain_cfg:
P = registry.resolve(pretrain_cfg, schema=ConfigSchemaPretrain)
P = registry.resolve(pretrain_cfg, schema=ConfigSchemaPretrain) # type: ignore[arg-type]
init_tok2vec(self, P, I)
self._link_components()
self._optimizer = sgd
@@ -1589,9 +1589,7 @@ class Language:
if batch_size is None:
batch_size = self.batch_size
pipes = (
[]
) # contains functools.partial objects to easily create multiprocess worker.
pipes = [] # contains functools.partial objects to easily create multiprocess worker.
for name, proc in self.pipeline:
if name in disable:
continue
@@ -1626,7 +1624,11 @@ class Language:
if name in disable or not is_trainable:
continue
if hasattr(proc, "model") and hasattr(proc.model, "ops") and isinstance(proc.model.ops, CupyOps): # type: ignore
if (
hasattr(proc, "model")
and hasattr(proc.model, "ops")
and isinstance(proc.model.ops, CupyOps)
): # type: ignore
return True
return False
@@ -1821,7 +1823,7 @@ class Language:
orig_pretraining = config.pop("pretraining", None)
config["components"] = {}
if auto_fill:
filled = registry.fill(config, validate=validate, schema=ConfigSchema)
filled = registry.fill(config, validate=validate, schema=ConfigSchema) # type: ignore[arg-type]
else:
filled = config
filled["components"] = orig_pipeline
@@ -1830,7 +1832,9 @@ class Language:
filled["pretraining"] = orig_pretraining
config["pretraining"] = orig_pretraining
resolved_nlp = registry.resolve(
filled["nlp"], validate=validate, schema=ConfigSchemaNlp
filled["nlp"],
validate=validate,
schema=ConfigSchemaNlp, # type: ignore[arg-type]
)
create_tokenizer = resolved_nlp["tokenizer"]
create_vectors = resolved_nlp["vectors"]
+3 -3
View File
@@ -85,7 +85,7 @@ class Table(OrderedDict):
value: The value to set.
"""
key = get_string_id(key)
OrderedDict.__setitem__(self, key, value) # type:ignore[assignment]
OrderedDict.__setitem__(self, key, value) # type: ignore[assignment]
self.bloom.add(key)
def set(self, key: Union[str, int], value: Any) -> None:
@@ -104,7 +104,7 @@ class Table(OrderedDict):
RETURNS: The value.
"""
key = get_string_id(key)
return OrderedDict.__getitem__(self, key) # type:ignore[index]
return OrderedDict.__getitem__(self, key) # type: ignore[index]
def get(self, key: Union[str, int], default: Optional[Any] = None) -> Any:
"""Get the value for a given key. String keys will be hashed.
@@ -114,7 +114,7 @@ class Table(OrderedDict):
RETURNS: The value.
"""
key = get_string_id(key)
return OrderedDict.get(self, key, default) # type:ignore[arg-type]
return OrderedDict.get(self, key, default) # type: ignore[arg-type]
def __contains__(self, key: Union[str, int]) -> bool: # type: ignore[override]
"""Check whether a key is in the table. String keys will be hashed.
+4 -2
View File
@@ -48,10 +48,12 @@ class DependencyMatcher:
*,
on_match: Optional[
Callable[[DependencyMatcher, Doc, int, List[Tuple[int, List[int]]]], Any]
] = ...
] = ...,
) -> None: ...
def has_key(self, key: Union[str, int]) -> bool: ...
def get(self, key: Union[str, int], default: Optional[Any] = ...) -> Tuple[
def get(
self, key: Union[str, int], default: Optional[Any] = ...
) -> Tuple[
Optional[
Callable[[DependencyMatcher, Doc, int, List[Tuple[int, List[int]]]], Any]
],
+3 -3
View File
@@ -33,7 +33,7 @@ class Matcher:
on_match: Optional[
Callable[[Matcher, Doc, int, List[Tuple[Any, ...]]], Any]
] = ...,
greedy: Optional[str] = ...
greedy: Optional[str] = ...,
) -> None: ...
def remove(self, key: str) -> None: ...
def has_key(self, key: Union[str, int]) -> bool: ...
@@ -56,7 +56,7 @@ class Matcher:
*,
as_spans: Literal[False] = ...,
allow_missing: bool = ...,
with_alignments: bool = ...
with_alignments: bool = ...,
) -> List[Tuple[int, int, int]]: ...
@overload
def __call__(
@@ -65,6 +65,6 @@ class Matcher:
*,
as_spans: Literal[True],
allow_missing: bool = ...,
with_alignments: bool = ...
with_alignments: bool = ...,
) -> List[Span]: ...
def _normalize_key(self, key: Any) -> Any: ...
+1 -1
View File
@@ -1,4 +1,4 @@
from typing import Any, Callable, Dict, List, Optional, Tuple, Union, overload
from typing import Any, Callable, List, Optional, Tuple, Union, overload
from ..compat import Literal
from ..tokens import Doc, Span
+1 -1
View File
@@ -57,7 +57,7 @@ cdef class PhraseMatcher:
attr = "ORTH"
if attr == "IS_SENT_START":
attr = "SENT_START"
if attr.lower() not in TokenPattern().dict():
if attr.lower() not in TokenPattern().model_dump():
raise ValueError(Errors.E152.format(attr=attr))
self.attr = IDS.get(attr)
-1
View File
@@ -4,7 +4,6 @@ from thinc.api import Model
from thinc.types import Floats2d
from ..tokens import Doc
from ..util import registry
def CharacterEmbed(nM: int, nC: int) -> Model[List[Doc], List[Floats2d]]:
-2
View File
@@ -1,7 +1,5 @@
from thinc.api import Model, normal_init
from ..util import registry
def PrecomputableAffine(nO, nI, nF, nP, dropout=0.1):
model = Model(
+1 -3
View File
@@ -2,14 +2,12 @@ import functools
import inspect
import types
import warnings
from typing import TYPE_CHECKING, Callable, Dict, List, Optional, Set, Type
from typing import TYPE_CHECKING, Callable, Dict, List, Optional, Set
from thinc.layers import with_nvtx_range
from thinc.model import Model, wrap_model_recursive
from thinc.util import use_nvtx_range
from ..errors import Warnings
from ..util import registry
if TYPE_CHECKING:
# This lets us add type hints for mypy etc. without causing circular imports
-1
View File
@@ -1,7 +1,6 @@
from thinc.api import Model
from ..attrs import LOWER
from ..util import registry
def extract_ngrams(ngram_size: int, attr: int = LOWER) -> Model:
-2
View File
@@ -3,8 +3,6 @@ from typing import Callable, List, Tuple
from thinc.api import Model, to_numpy
from thinc.types import Ints1d, Ragged
from ..util import registry
def extract_spans() -> Model[Tuple[Ragged, Ragged], Ragged]:
"""Extract spans from a sequence of source arrays, as specified by an array
+1 -1
View File
@@ -1,6 +1,6 @@
from typing import Callable, List, Tuple, Union
from thinc.api import Model, registry
from thinc.api import Model
from thinc.types import Ints2d
from ..tokens import Doc
+3 -4
View File
@@ -23,7 +23,6 @@ from ...kb import (
get_candidates_batch,
)
from ...tokens import Doc, Span
from ...util import registry
from ...vocab import Vocab
from ..extract_spans import extract_spans
@@ -122,7 +121,7 @@ def create_candidates() -> Callable[[KnowledgeBase, Span], Iterable[Candidate]]:
return get_candidates
def create_candidates_batch() -> (
Callable[[KnowledgeBase, Iterable[Span]], Iterable[Iterable[Candidate]]]
):
def create_candidates_batch() -> Callable[
[KnowledgeBase, Iterable[Span]], Iterable[Iterable[Candidate]]
]:
return get_candidates_batch
+3 -3
View File
@@ -1,5 +1,5 @@
from functools import partial
from typing import TYPE_CHECKING, Any, Callable, Iterable, List, Optional, Tuple, cast
from typing import TYPE_CHECKING, Callable, Iterable, List, Optional, Tuple, cast
import numpy
from thinc.api import (
@@ -21,7 +21,7 @@ from thinc.types import Floats2d, Ints1d
from ...attrs import ID, ORTH
from ...errors import Errors
from ...util import OOV_RANK, registry
from ...util import OOV_RANK
from ...vectors import Mode as VectorsMode
if TYPE_CHECKING:
@@ -199,7 +199,7 @@ def build_masked_language_model(
layers=[wrapped_model],
init=mlm_initialize,
refs={"wrapped": wrapped_model},
dims={dim: None for dim in wrapped_model.dim_names},
dims=dict.fromkeys(wrapped_model.dim_names),
)
mlm_model.set_ref("wrapped", wrapped_model)
return mlm_model
+1 -2
View File
@@ -1,4 +1,4 @@
from typing import List, Optional, cast
from typing import List, Optional
from thinc.api import Linear, Model, chain, list2array, use_ops, zero_init
from thinc.types import Floats2d
@@ -6,7 +6,6 @@ from thinc.types import Floats2d
from ...compat import Literal
from ...errors import Errors
from ...tokens import Doc
from ...util import registry
from .._precomputable_affine import PrecomputableAffine
from ..tb_framework import TransitionModel
-1
View File
@@ -4,7 +4,6 @@ from thinc.api import Model, chain, with_array
from thinc.types import Floats1d, Floats2d
from ...tokens import Doc
from ...util import registry
InT = List[Doc]
OutT = Floats2d
-1
View File
@@ -18,7 +18,6 @@ from thinc.api import (
from thinc.types import Floats2d, Ragged
from ...tokens import Doc
from ...util import registry
from ..extract_spans import extract_spans
-1
View File
@@ -4,7 +4,6 @@ from thinc.api import Model, Softmax_v2, chain, with_array, zero_init
from thinc.types import Floats2d
from ...tokens import Doc
from ...util import registry
def build_tagger_model(
-1
View File
@@ -36,7 +36,6 @@ from thinc.types import ArrayXd, Floats2d
from ...attrs import ORTH
from ...errors import Errors
from ...tokens import Doc
from ...util import registry
from ..extract_ngrams import extract_ngrams
from ..staticvectors import StaticVectors
from .tok2vec import get_tok2vec_width
+1 -2
View File
@@ -17,14 +17,13 @@ from thinc.api import (
with_array,
with_padded,
)
from thinc.types import Floats2d, Ints1d, Ints2d, Ragged
from thinc.types import Floats2d, Ints2d, Ragged
from ...attrs import intify_attr
from ...errors import Errors
from ...ml import _character_embed
from ...pipeline.tok2vec import Tok2VecListener
from ...tokens import Doc
from ...util import registry
from ..featureextractor import FeatureExtractor
from ..staticvectors import StaticVectors
+3 -3
View File
@@ -1,7 +1,7 @@
import warnings
from typing import Callable, List, Optional, Sequence, Tuple, cast
from typing import Callable, List, Optional, Tuple, cast
from thinc.api import Model, Ops, registry
from thinc.api import Model, Ops
from thinc.initializers import glorot_uniform_init
from thinc.types import Floats1d, Floats2d, Ints1d, Ragged
from thinc.util import partial
@@ -19,7 +19,7 @@ def StaticVectors(
*,
dropout: Optional[float] = None,
init_W: Callable = glorot_uniform_init,
key_attr: str = "ORTH"
key_attr: str = "ORTH",
) -> Model[List[Doc], Ragged]:
"""Embed Doc objects with their vocab's vectors table, applying a learned
linear projection to control the dimensionality. If a dropout rate is
-1
View File
@@ -1,6 +1,5 @@
from thinc.api import Model, noop
from ..util import registry
from .parser_model import ParserStepModel
+2 -2
View File
@@ -23,7 +23,7 @@ def validate_attrs(values: Iterable[str]) -> Iterable[str]:
values (Iterable[str]): The string attributes to check, e.g. `["token.pos"]`.
RETURNS (Iterable[str]): The checked attributes.
"""
data = dot_to_dict({value: True for value in values})
data = dot_to_dict(dict.fromkeys(values, True))
objs = {"doc": Doc, "token": Token, "span": Span}
for obj_key, attrs in data.items():
if obj_key == "span":
@@ -100,7 +100,7 @@ def analyze_pipes(
all_attrs.update(meta.requires)
result["summary"][name] = {key: getattr(meta, key, None) for key in keys}
prev_pipes = nlp.pipeline[:i]
requires = {annot: False for annot in meta.requires}
requires = dict.fromkeys(meta.requires, False)
if requires:
for prev_name, prev_pipe in prev_pipes:
prev_meta = nlp.get_pipe_meta(prev_name)
+15 -13
View File
@@ -1,12 +1,16 @@
from collections import defaultdict
from typing import Any, Dict, List, Union
try:
from pydantic.v1 import BaseModel, Field, ValidationError
from pydantic.v1.types import StrictBool, StrictInt, StrictStr
except ImportError:
from pydantic import BaseModel, Field, ValidationError # type: ignore
from pydantic.types import StrictBool, StrictInt, StrictStr # type: ignore
from pydantic import (
BaseModel,
ConfigDict,
Field,
RootModel,
StrictBool,
StrictInt,
StrictStr,
ValidationError,
)
class MatchNodeSchema(BaseModel):
@@ -15,20 +19,18 @@ class MatchNodeSchema(BaseModel):
prefix_tree: StrictInt = Field(..., title="Prefix tree")
suffix_tree: StrictInt = Field(..., title="Suffix tree")
class Config:
extra = "forbid"
model_config = ConfigDict(extra="forbid")
class SubstNodeSchema(BaseModel):
orig: Union[int, StrictStr] = Field(..., title="Original substring")
subst: Union[int, StrictStr] = Field(..., title="Replacement substring")
class Config:
extra = "forbid"
model_config = ConfigDict(extra="forbid")
class EditTreeSchema(BaseModel):
__root__: Union[MatchNodeSchema, SubstNodeSchema]
class EditTreeSchema(RootModel[Union[MatchNodeSchema, SubstNodeSchema]]):
pass
def validate_edit_tree(obj: Dict[str, Any]) -> List[str]:
@@ -38,7 +40,7 @@ def validate_edit_tree(obj: Dict[str, Any]) -> List[str]:
RETURNS (List[str]): A list of error messages, if available.
"""
try:
EditTreeSchema.parse_obj(obj)
EditTreeSchema.model_validate(obj)
return []
except ValidationError as e:
errors = e.errors()
+6 -4
View File
@@ -1,5 +1,4 @@
import importlib
import sys
from pathlib import Path
from typing import Any, Callable, Dict, Iterable, List, Optional, Tuple, Union
@@ -14,12 +13,14 @@ from ..symbols import IDS
from ..tokens import Doc, Span
from ..tokens._retokenize import normalize_token_attrs, set_token_attrs
from ..training import Example
from ..util import SimpleFrozenList, registry
from ..util import SimpleFrozenList
from ..vocab import Vocab
from .pipe import Pipe
MatcherPatternType = List[Dict[Union[int, str], Any]]
AttributeRulerPatternType = Dict[str, Union[MatcherPatternType, Dict, int]]
AttributeRulerPatternType = Dict[
str, Union[List[MatcherPatternType], MatcherPatternType, Dict, int]
]
TagMapType = Dict[str, Dict[Union[int, str], Union[int, str]]]
MorphRulesType = Dict[str, Dict[str, Dict[Union[int, str], Union[int, str]]]]
@@ -137,7 +138,8 @@ class AttributeRuler(Pipe):
matches = self.matcher(doc, allow_missing=True, as_spans=False)
# Sort by the attribute ID, so that later rules have precedence
matches = [
(int(self.vocab.strings[m_id]), m_id, s, e) for m_id, s, e in matches # type: ignore
(int(self.vocab.strings[m_id]), m_id, s, e)
for m_id, s, e in matches # type: ignore
]
matches.sort()
return matches
-1
View File
@@ -1,5 +1,4 @@
import importlib
import sys
from collections import Counter
from itertools import islice
from typing import Any, Callable, Dict, Iterable, List, Optional, Tuple, cast
+1 -3
View File
@@ -1,6 +1,5 @@
import importlib
import random
import sys
from itertools import islice
from pathlib import Path
from typing import Any, Callable, Dict, Iterable, List, Optional, Union
@@ -16,9 +15,8 @@ from ..language import Language
from ..scorer import Scorer
from ..tokens import Doc, Span
from ..training import Example, validate_examples, validate_get_examples
from ..util import SimpleFrozenList, registry
from ..util import SimpleFrozenList
from ..vocab import Vocab
from .legacy.entity_linker import EntityLinker_v1
from .pipe import deserialize_config
from .trainable_pipe import TrainablePipe
+1 -2
View File
@@ -1,5 +1,4 @@
import importlib
import sys
import warnings
from collections import defaultdict
from pathlib import Path
@@ -14,7 +13,7 @@ from ..matcher.levenshtein import levenshtein_compare
from ..scorer import get_ner_prf
from ..tokens import Doc, Span
from ..training import Example
from ..util import SimpleFrozenList, ensure_path, from_disk, registry, to_disk
from ..util import SimpleFrozenList, ensure_path, from_disk, to_disk
from .pipe import Pipe
DEFAULT_ENT_ID_SEP = "||"
+3 -2
View File
@@ -14,9 +14,10 @@ from ..pipeline.edit_tree_lemmatizer import (
)
# Import factory default configurations
from ..pipeline.entity_linker import DEFAULT_NEL_MODEL, EntityLinker, EntityLinker_v1
from ..pipeline.entity_linker import DEFAULT_NEL_MODEL, EntityLinker
from ..pipeline.entityruler import DEFAULT_ENT_ID_SEP, EntityRuler
from ..pipeline.functions import DocCleaner, TokenSplitter
from ..pipeline.legacy import EntityLinker_v1
from ..pipeline.lemmatizer import Lemmatizer
from ..pipeline.morphologizer import DEFAULT_MORPH_MODEL, Morphologizer
from ..pipeline.multitask import DEFAULT_MT_MODEL, MultitaskObjective
@@ -24,8 +25,8 @@ from ..pipeline.ner import DEFAULT_NER_MODEL, EntityRecognizer
from ..pipeline.sentencizer import Sentencizer
from ..pipeline.senter import DEFAULT_SENTER_MODEL, SentenceRecognizer
from ..pipeline.span_finder import DEFAULT_SPAN_FINDER_MODEL, SpanFinder
from ..pipeline.span_ruler import DEFAULT_SPANS_KEY as SPAN_RULER_DEFAULT_SPANS_KEY
from ..pipeline.span_ruler import (
DEFAULT_SPANS_KEY as SPAN_RULER_DEFAULT_SPANS_KEY,
SpanRuler,
prioritize_existing_ents_filter,
prioritize_new_ents_filter,
-1
View File
@@ -1,5 +1,4 @@
import importlib
import sys
import warnings
from typing import Any, Dict
+1 -2
View File
@@ -1,5 +1,4 @@
import importlib
import sys
import warnings
from pathlib import Path
from typing import Any, Callable, Dict, Iterable, List, Optional, Tuple, Union
@@ -13,7 +12,7 @@ from ..lookups import Lookups, load_lookups
from ..scorer import Scorer
from ..tokens import Doc, Token
from ..training import Example
from ..util import SimpleFrozenList, logger, registry
from ..util import SimpleFrozenList, logger
from ..vocab import Vocab
from .pipe import Pipe
-1
View File
@@ -7,7 +7,6 @@ from typing import (
Iterator,
List,
NoReturn,
Optional,
Tuple,
Union,
)
-2
View File
@@ -1,5 +1,4 @@
import importlib
import sys
from typing import Any, Callable, Dict, Iterable, List, Optional, Tuple
from thinc.api import Config, Model, Optimizer, set_dropout_rate
@@ -10,7 +9,6 @@ from ..language import Language
from ..scorer import Scorer
from ..tokens import Doc, Span
from ..training import Example
from ..util import registry
from .spancat import DEFAULT_SPANS_KEY
from .trainable_pipe import TrainablePipe
+1 -2
View File
@@ -1,5 +1,4 @@
import importlib
import sys
import warnings
from functools import partial
from pathlib import Path
@@ -27,7 +26,7 @@ from ..matcher.levenshtein import levenshtein_compare
from ..scorer import Scorer
from ..tokens import Doc, Span
from ..training import Example
from ..util import SimpleFrozenList, ensure_path, registry
from ..util import SimpleFrozenList, ensure_path
from .pipe import Pipe
PatternType = Dict[str, Union[str, List[Dict[str, Any]]]]
-2
View File
@@ -1,5 +1,4 @@
import importlib
import sys
from dataclasses import dataclass
from functools import partial
from typing import Any, Callable, Dict, Iterable, List, Optional, Tuple, Union, cast
@@ -14,7 +13,6 @@ from ..language import Language
from ..scorer import Scorer
from ..tokens import Doc, Span, SpanGroup
from ..training import Example, validate_examples
from ..util import registry
from ..vocab import Vocab
from .trainable_pipe import TrainablePipe
+1 -4
View File
@@ -1,18 +1,15 @@
import importlib
import sys
from itertools import islice
from typing import Any, Callable, Dict, Iterable, List, Optional, Tuple
import numpy
from thinc.api import Config, Model, Optimizer, get_array_module, set_dropout_rate
from thinc.types import Floats2d
from thinc.api import Config, Model, Optimizer, set_dropout_rate
from ..errors import Errors
from ..language import Language
from ..scorer import Scorer
from ..tokens import Doc
from ..training import Example, validate_examples, validate_get_examples
from ..util import registry
from ..vocab import Vocab
from .trainable_pipe import TrainablePipe
+1 -5
View File
@@ -1,17 +1,13 @@
import importlib
import sys
from itertools import islice
from typing import Any, Callable, Dict, Iterable, List, Optional
from typing import Any, Callable, Dict, Iterable, Optional
from thinc.api import Config, Model
from thinc.types import Floats2d
from ..errors import Errors
from ..language import Language
from ..scorer import Scorer
from ..tokens import Doc
from ..training import Example, validate_get_examples
from ..util import registry
from ..vocab import Vocab
from .textcat import TextCategorizer
-1
View File
@@ -1,5 +1,4 @@
import importlib
import sys
from itertools import islice
from typing import Any, Callable, Dict, Iterable, List, Optional, Sequence
+43 -77
View File
@@ -1,5 +1,4 @@
import inspect
import re
from collections import defaultdict
from enum import Enum
from typing import (
@@ -16,34 +15,19 @@ from typing import (
Union,
)
try:
from pydantic.v1 import (
BaseModel,
ConstrainedStr,
Field,
StrictBool,
StrictFloat,
StrictInt,
StrictStr,
ValidationError,
create_model,
validator,
)
from pydantic.v1.main import ModelMetaclass
except ImportError:
from pydantic import ( # type: ignore
BaseModel,
ConstrainedStr,
Field,
StrictBool,
StrictFloat,
StrictInt,
StrictStr,
ValidationError,
create_model,
validator,
)
from pydantic.main import ModelMetaclass # type: ignore
from pydantic import (
BaseModel,
ConfigDict,
Field,
StrictBool,
StrictFloat,
StrictInt,
StrictStr,
ValidationError,
constr,
create_model,
field_validator,
)
from thinc.api import ConfigValidationError, Model, Optimizer
from thinc.config import Promise
@@ -89,14 +73,9 @@ def validate(schema: Type[BaseModel], obj: Dict[str, Any]) -> List[str]:
# Initialization
class ArgSchemaConfig:
extra = "forbid"
arbitrary_types_allowed = True
ArgSchemaConfig = ConfigDict(extra="forbid", arbitrary_types_allowed=True)
class ArgSchemaConfigExtra:
extra = "forbid"
arbitrary_types_allowed = True
ArgSchemaConfigExtra = ConfigDict(extra="forbid", arbitrary_types_allowed=True)
def get_arg_model(
@@ -105,7 +84,7 @@ def get_arg_model(
exclude: Iterable[str] = tuple(),
name: str = "ArgModel",
strict: bool = True,
) -> ModelMetaclass:
) -> type[BaseModel]:
"""Generate a pydantic model for function arguments.
func (Callable): The function to generate the schema for.
@@ -113,7 +92,7 @@ def get_arg_model(
name (str): Name of created model class.
strict (bool): Don't allow extra arguments if no variable keyword arguments
are allowed on the function.
RETURNS (ModelMetaclass): A pydantic model.
RETURNS (type[BaseModel]): A pydantic model.
"""
sig_args = {}
try:
@@ -167,7 +146,7 @@ def validate_init_settings(
"""
schema = get_arg_model(func, exclude=exclude, name="InitArgModel")
try:
return schema(**settings).dict()
return schema.model_validate(settings).model_dump()
except ValidationError as e:
block = "initialize" if not section else f"initialize.{section}"
title = f"Error validating initialization settings in [{block}]"
@@ -228,11 +207,10 @@ class TokenPatternString(BaseModel):
None, alias="fuzzy9"
)
class Config:
extra = "forbid"
allow_population_by_field_name = True # allow alias and field name
model_config = ConfigDict(extra="forbid", populate_by_name=True)
@validator("*", pre=True, each_item=True, allow_reuse=True)
@field_validator("*", mode="before")
@classmethod
def raise_for_none(cls, v):
if v is None:
raise ValueError("None / null is not allowed")
@@ -253,11 +231,10 @@ class TokenPatternNumber(BaseModel):
GT: Optional[Union[StrictInt, StrictFloat]] = Field(None, alias=">")
LT: Optional[Union[StrictInt, StrictFloat]] = Field(None, alias="<")
class Config:
extra = "forbid"
allow_population_by_field_name = True # allow alias and field name
model_config = ConfigDict(extra="forbid", populate_by_name=True)
@validator("*", pre=True, each_item=True, allow_reuse=True)
@field_validator("*", mode="before")
@classmethod
def raise_for_none(cls, v):
if v is None:
raise ValueError("None / null is not allowed")
@@ -271,11 +248,10 @@ class TokenPatternOperatorSimple(str, Enum):
exclamation: StrictStr = StrictStr("!")
class TokenPatternOperatorMinMax(ConstrainedStr):
regex = re.compile(r"^({\d+}|{\d+,\d*}|{\d*,\d+})$")
TokenPatternOperatorMinMax = constr(pattern=r"^(\{\d+\}|\{\d+,\d*\}|\{\d*,\d+\})$")
TokenPatternOperator = Union[TokenPatternOperatorSimple, TokenPatternOperatorMinMax]
TokenPatternOperator = Union[TokenPatternOperatorSimple, TokenPatternOperatorMinMax] # type: ignore[valid-type]
StringValue = Union[TokenPatternString, StrictStr]
NumberValue = Union[TokenPatternNumber, StrictInt, StrictFloat]
UnderscoreValue = Union[
@@ -323,12 +299,14 @@ class TokenPattern(BaseModel):
op: Optional[TokenPatternOperator] = None
underscore: Optional[Dict[StrictStr, UnderscoreValue]] = Field(None, alias="_")
class Config:
extra = "forbid"
allow_population_by_field_name = True
alias_generator = lambda value: value.upper()
model_config = ConfigDict(
extra="forbid",
populate_by_name=True,
alias_generator=lambda value: value.upper(),
)
@validator("*", pre=True, allow_reuse=True)
@field_validator("*", mode="before")
@classmethod
def raise_for_none(cls, v):
if v is None:
raise ValueError("None / null is not allowed")
@@ -336,10 +314,9 @@ class TokenPattern(BaseModel):
class TokenPatternSchema(BaseModel):
pattern: List[TokenPattern] = Field(..., min_items=1)
pattern: List[TokenPattern] = Field(..., min_length=1)
class Config:
extra = "forbid"
model_config = ConfigDict(extra="forbid")
# Model meta
@@ -397,9 +374,7 @@ class ConfigSchemaTraining(BaseModel):
before_update: Optional[Callable[["Language", Dict[str, Any]], None]] = Field(..., title="Optional callback that is invoked at the start of each training step")
# fmt: on
class Config:
extra = "forbid"
arbitrary_types_allowed = True
model_config = ConfigDict(extra="forbid", arbitrary_types_allowed=True)
class ConfigSchemaNlp(BaseModel):
@@ -415,14 +390,11 @@ class ConfigSchemaNlp(BaseModel):
vectors: Callable = Field(..., title="Vectors implementation")
# fmt: on
class Config:
extra = "forbid"
arbitrary_types_allowed = True
model_config = ConfigDict(extra="forbid", arbitrary_types_allowed=True)
class ConfigSchemaPretrainEmpty(BaseModel):
class Config:
extra = "forbid"
model_config = ConfigDict(extra="forbid")
class ConfigSchemaPretrain(BaseModel):
@@ -439,9 +411,7 @@ class ConfigSchemaPretrain(BaseModel):
objective: Callable[["Vocab", Model], Model] = Field(..., title="A function that creates the pretraining objective.")
# fmt: on
class Config:
extra = "forbid"
arbitrary_types_allowed = True
model_config = ConfigDict(extra="forbid", arbitrary_types_allowed=True)
class ConfigSchemaInit(BaseModel):
@@ -450,15 +420,13 @@ class ConfigSchemaInit(BaseModel):
lookups: Optional[Lookups] = Field(..., title="Vocabulary lookups, e.g. lexeme normalization")
vectors: Optional[StrictStr] = Field(..., title="Path to vectors")
init_tok2vec: Optional[StrictStr] = Field(..., title="Path to pretrained tok2vec weights")
tokenizer: Dict[StrictStr, Any] = Field(..., help="Arguments to be passed into Tokenizer.initialize")
components: Dict[StrictStr, Dict[StrictStr, Any]] = Field(..., help="Arguments for TrainablePipe.initialize methods of pipeline components, keyed by component")
tokenizer: Dict[StrictStr, Any] = Field(..., title="Arguments to be passed into Tokenizer.initialize")
components: Dict[StrictStr, Dict[StrictStr, Any]] = Field(..., title="Arguments for TrainablePipe.initialize methods of pipeline components, keyed by component")
before_init: Optional[Callable[["Language"], "Language"]] = Field(..., title="Optional callback to modify nlp object before initialization")
after_init: Optional[Callable[["Language"], "Language"]] = Field(..., title="Optional callback to modify nlp object after initialization")
# fmt: on
class Config:
extra = "forbid"
arbitrary_types_allowed = True
model_config = ConfigDict(extra="forbid", arbitrary_types_allowed=True)
class ConfigSchema(BaseModel):
@@ -469,9 +437,7 @@ class ConfigSchema(BaseModel):
corpora: Dict[str, Reader]
initialize: ConfigSchemaInit
class Config:
extra = "allow"
arbitrary_types_allowed = True
model_config = ConfigDict(extra="allow", arbitrary_types_allowed=True)
CONFIG_SCHEMAS = {
+7 -5
View File
@@ -205,11 +205,13 @@ cdef class StringStore:
if mem is None:
mem = Pool()
self.mem = mem
yield mem
for key in self._transient_keys:
map_clear(self._map.c_map, key)
self._transient_keys.clear()
self.mem = self._non_temp_mem
try:
yield mem
finally:
for key in self._transient_keys:
map_clear(self._map.c_map, key)
self._transient_keys.clear()
self.mem = self._non_temp_mem
def add(self, string: str, allow_transient: Optional[bool] = None) -> int:
"""Add a string to the StringStore.
+4 -4
View File
@@ -26,7 +26,7 @@ def test_create_from_words_and_text(vocab):
# no whitespace in words
words = ["'", "dogs", "'", "run"]
text = " 'dogs'\n\nrun "
(words, spaces) = util.get_words_and_spaces(words, text)
words, spaces = util.get_words_and_spaces(words, text)
doc = Doc(vocab, words=words, spaces=spaces)
assert [t.text for t in doc] == [" ", "'", "dogs", "'", "\n\n", "run", " "]
assert [t.whitespace_ for t in doc] == ["", "", "", "", "", " ", ""]
@@ -38,7 +38,7 @@ def test_create_from_words_and_text(vocab):
# partial whitespace in words
words = [" ", "'", "dogs", "'", "\n\n", "run", " "]
text = " 'dogs'\n\nrun "
(words, spaces) = util.get_words_and_spaces(words, text)
words, spaces = util.get_words_and_spaces(words, text)
doc = Doc(vocab, words=words, spaces=spaces)
assert [t.text for t in doc] == [" ", "'", "dogs", "'", "\n\n", "run", " "]
assert [t.whitespace_ for t in doc] == ["", "", "", "", "", " ", ""]
@@ -50,7 +50,7 @@ def test_create_from_words_and_text(vocab):
# non-standard whitespace tokens
words = [" ", " ", "'", "dogs", "'", "\n\n", "run"]
text = " 'dogs'\n\nrun "
(words, spaces) = util.get_words_and_spaces(words, text)
words, spaces = util.get_words_and_spaces(words, text)
doc = Doc(vocab, words=words, spaces=spaces)
assert [t.text for t in doc] == [" ", "'", "dogs", "'", "\n\n", "run", " "]
assert [t.whitespace_ for t in doc] == ["", "", "", "", "", " ", ""]
@@ -63,7 +63,7 @@ def test_create_from_words_and_text(vocab):
with pytest.raises(ValueError):
words = [" ", " ", "'", "dogs", "'", "\n\n", "run"]
text = " 'dogs'\n\nrun "
(words, spaces) = util.get_words_and_spaces(words + ["away"], text)
words, spaces = util.get_words_and_spaces(words + ["away"], text)
def test_create_with_heads_and_no_deps(vocab):
+3 -3
View File
@@ -60,12 +60,12 @@ def test_issue1757():
"""Test comparison against None doesn't cause segfault."""
doc = Doc(Vocab(), words=["a", "b", "c"])
assert not doc[0] < None
assert not doc[0] is None
assert doc[0] is not None
assert doc[0] >= None
assert not doc[:2] < None
assert not doc[:2] is None
assert doc[:2] is not None
assert doc[:2] >= None
assert not doc.vocab["a"] is None
assert doc.vocab["a"] is not None
assert not doc.vocab["a"] < None
-3
View File
@@ -1,6 +1,3 @@
import pytest
def test_bg_tokenizer_handles_final_diacritics(bg_tokenizer):
text = "Ня̀маше яйца̀. Ня̀маше яйца̀."
tokens = bg_tokenizer(text)
+5 -5
View File
@@ -48,13 +48,13 @@ from spacy.tokens import Doc
[(0,4)]
),
# Tengo un gato y un perro -> un gato, un perro
(
(
["Tengo", "un", "gato", "y", "un", "perro"],
[0, 2, 0, 5, 5, 0],
["ROOT", "det", "obj", "cc", "det", "conj"],
["VERB", "DET", "NOUN", "CCONJ", "DET", "NOUN"],
[(1,3), (4,6)]
),
# Dom Pedro II -> Dom Pedro II
(
@@ -101,11 +101,11 @@ from spacy.tokens import Doc
[1, 1, 3, 1, 5, 1],
['det', 'ROOT', 'case', 'nmod', 'case', 'nmod'],
['DET', 'NOUN', 'ADP', 'PROPN', 'ADP', 'NOUN'],
[(0,2), (3,4), (5,6)]
[(0,2), (3,4), (5,6)]
),
# El gato regordete de Susana y su amigo -> el gato regordete, Susana, su amigo
(
(
['El', 'gato', 'regordete', 'de', 'Susana', 'y', 'su', 'amigo'],
[1, 1, 1, 4, 1, 7, 7, 1],
['det', 'ROOT', 'amod', 'case', 'nmod', 'cc', 'det', 'conj'],
+1 -2
View File
@@ -2,8 +2,7 @@ import pytest
ET_BASIC_TOKENIZATION_TESTS = [
(
"Kedagi ei või piinata ega ebainimlikult või alandavalt kohelda "
"ega karistada.",
"Kedagi ei või piinata ega ebainimlikult või alandavalt kohelda ega karistada.",
[
"Kedagi",
"ei",

Some files were not shown because too many files have changed in this diff Show More