Compare commits

...

210 Commits

Author SHA1 Message Date
Matthew Honnibal f69c32f7a0 Install click with previous spaCy in upgrade test
spacy <=3.8.14 imports click without declaring it (#13971), and modern
typer no longer depends on click, so the pre-upgrade install fails on
import without it.
2026-08-07 13:44:28 +02:00
Matthew Honnibal 29bc4f2a5a Fix release smoke/upgrade tests
- Download wheels from the run's own artifacts: the release created by
  build_wheels is a draft, and drafts can't be looked up by tag, so the
  release-downloader step always 404'd
- Drop the checkout: the source tree shadowed the installed wheel when
  running python from the workspace root (No module named spacy.symbols)
- Strip the release-v prefix from the tag before using it as a pip
  version specifier
2026-08-07 12:12:52 +02:00
Matthew Honnibal 86edb26d60 Build linux wheels in manylinux_2_28 containers
numpy >=2.3 only publishes manylinux_2_28 wheels, so pip inside the
manylinux2014 build container falls back to compiling numpy from
source, which fails (container GCC is too old for numpy's meson
build). Bump the x86_64 and aarch64 build images to manylinux_2_28,
matching numpy's own baseline.
2026-08-07 10:17:55 +02:00
Matthew Honnibal 7a64151619 Cap hypothesis below 6.156 to keep win_arm64 wheel builds working 2026-08-07 09:58:05 +02:00
Matthew Honnibal 768e8d572c Fix CI: bump mypy pin for numpy 2.5 stubs, sync confection pin
mypy 1.5.x crashes with an internal error on numpy>=2.3 type stubs,
which killed the mypy step on Python 3.12+. Bump to mypy 1.20.x and
fix the type errors the newer mypy reports.

Also sync the confection pin in requirements.txt with setup.cfg
(>=1.3.2), which was the single test failure on Python 3.10/3.11.
2026-08-07 09:34:11 +02:00
Matthew Honnibal edd3d0fcca Format tests README code blocks for ruff 0.16 markdown formatting 2026-08-07 09:13:50 +02:00
Matthew Honnibal 82d98b2d96 Update version 2026-08-06 23:03:15 +02:00
Matthew Honnibal 47ce793778 Merge branch 'master' of https://github.com/explosion/spaCy 2026-08-06 23:02:36 +02:00
Mart Ratas dbe520e702 Fix click dependency issue (#13971) (#13973)
* Add click dependency in requirements.txt

* Add click dependency in setup.cfg
2026-08-06 22:59:47 +02:00
Ines Montani e67199550e Update README.md [ci skip] 2026-05-19 08:48:33 +02:00
Ines Montani 9af7b84777 Add note about Ellf [ci skip] 2026-05-19 08:48:27 +02:00
Matthew Honnibal 0f7e41d148 Add release notes for v3.8.14 2026-03-29 09:22:47 +02:00
Matthew Honnibal 0069cf99b6 Set version to 3.8.14 2026-03-28 21:59:08 +01:00
Taylor Satula 56032260f2 fix: check pip module availability instead of PATH binary in download (#13947)
_get_pip_install_cmd() uses shutil.which("pip") to check for pip, then
returns [sys.executable, "-m", "pip", "install"] which invokes pip as a
module. The binary check fails in venvs where ensurepip creates pip3 but
not pip (common on Debian/Ubuntu), even though python -m pip works.

Replace shutil.which("pip") with importlib.util.find_spec("pip"), which
tests the actual precondition: whether pip is importable in the current
interpreter. The uv branch is unchanged since uv is a standalone binary.

Fixes #13946
2026-03-28 21:56:56 +01:00
Matthew Honnibal d4bb796b5e Add least-privilege permissions to CI workflow 2026-03-27 08:49:43 +01:00
Matthew Honnibal 9d29209d04 Pass github context via stdin instead of CLI arg 2026-03-27 08:49:41 +01:00
Matthew Honnibal 4216738cf8 Pin GitHub Actions to commit SHAs for supply chain security 2026-03-26 15:48:47 +01:00
Matthew Honnibal 297938e704 Add smoke test and upgrade test to release build workflow
After wheels are built:
- smoke_test: install from wheel, download en_core_web_sm, verify entities
- upgrade_test: install previous spacy, download model, upgrade from wheel,
  verify model still loads and produces entities
2026-03-23 18:46:43 +01:00
Matthew Honnibal fdca647b1b Set version to 3.8.13 2026-03-23 16:52:52 +01:00
Matthew Honnibal 0d94a9d66d Pin confection>=1.3.2 — older versions crash with pydantic v2 2026-03-23 16:50:20 +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
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 453732d32d Format (#13929) 2026-03-03 09:56:06 +01:00
Nathan Goldbaum c1e7cb2ebf Merge pull request #13865 from reonokiy/fix-ipython-display-import 2025-11-27 11:55:45 -07:00
reonokiy b85cf89b0b chore(ipython): import from IPython.display instead of IPython.core.display 2025-11-27 12:20:20 +08:00
Matthew Honnibal e7a662acf8 Skip Python 3.10 on Windows ARM 2025-11-17 18:21:33 +01:00
Matthew Honnibal f628c69bdb Increment version 2025-11-17 18:21:16 +01:00
Matthew Honnibal e7d1c3a30d Windows arm needs to be disabled at the ci level, so remove this skip selector 2025-11-17 14:41:17 +01:00
Matthew Honnibal c273c231e1 Try again to fix the skip selector 2025-11-17 14:36:48 +01:00
Matthew Honnibal 160d72852e Try again to skip windows arm 2025-11-17 14:29:41 +01:00
Matthew Honnibal 75f1160c8c Skip windows arm 2025-11-17 14:23:39 +01:00
Matthew Honnibal 71e938dbf7 Skip windows arm 2025-11-17 14:11:25 +01:00
Matthew Honnibal 7abd196000 Set version to 3.8.10 2025-11-17 13:19:29 +01:00
Matthew Honnibal a24bb01613 Support python 3.14 2025-11-17 13:19:11 +01:00
Matthew Honnibal 305ffd5560 Fix cdef declaration for cython 3 2025-11-13 14:25:38 +01:00
Matthew Honnibal 6d386bf707 Skip building free-threaded 2025-11-13 14:24:43 +01:00
Matthew Honnibal a534b43ced Fix wheel path name on cibuildwheel 2025-11-13 14:23:55 +01:00
Matthew Honnibal c9d77932f9 Update pyproject.yml 2025-11-10 11:56:03 +01:00
Matthew Honnibal b49c537a0b Update version 2025-11-10 11:54:00 +01:00
Matthew Honnibal 09b4eb4ebe Use reuseable gha 2025-11-10 11:53:30 +01:00
Matthew Honnibal 38056e9012 Disable 3.9 2025-11-06 01:55:38 +01:00
Matthew Honnibal 2d7f850676 Increment version 2025-11-05 14:57:37 +01:00
Matthew Honnibal b0ba71d4e7 Update weasel dependency 2025-11-05 14:57:08 +01:00
Matthew Honnibal ac95fc541c Update weasel dependency 2025-11-05 14:56:48 +01:00
Matthew Honnibal d01a180a7f Update build matrix for tests 2025-11-05 10:26:48 +01:00
Matthew Honnibal 68bf84ec5c Fix type errors 2025-11-05 10:10:42 +01:00
Matthew Honnibal 4ebe774120 isort 2025-11-04 15:50:26 +01:00
Matthew Honnibal 352d774cb7 Update black 2025-11-04 15:19:56 +01:00
Matthew Honnibal 54f54fc4cc Reformat with black 25 2025-11-04 15:19:43 +01:00
Etienne.bfx 68679d6f85 Add custom download URL (#13848)
* add download url

* Update .pre-commit-config.yaml

* Update cli.mdx

---------

Co-authored-by: ebonnafoux <etienne.bonnafoux@gmail.com>
2025-10-28 09:43:47 +01:00
Marwan Mohammed Sayed 94d6be8a9b Fixed the import issue in displacy/__init__.py (#13876)
IPython deprecated IPython.core.display.display. The new one became IPython.display.display. So, I just fixed the import issue based on the new changes in IPython
2025-10-28 09:43:08 +01:00
Matthew Hernandez f5d04868e1 Update _util.py to fix Deprecation Warning (#13844)
Fixes issue 13843 involving a Deprecation Warning with the python package Click.
2025-10-28 09:42:23 +01:00
OMOTAYO OMOYEMI f40ceb5378 docs(website): remove spaCy Quickstart from Universe/Courses due to spam redirect (fixes #13853) (#13877) 2025-10-28 09:41:50 +01:00
Jeff Adolphe 41e07772dc Added Haitian Creole (ht) Language Support to spaCy (#13807)
This PR adds official support for Haitian Creole (ht) to spaCy's spacy/lang module.
It includes:

    Added all core language data files for spacy/lang/ht:
        tokenizer_exceptions.py
        punctuation.py
        lex_attrs.py
        syntax_iterators.py
        lemmatizer.py
        stop_words.py
        tag_map.py

    Unit tests for tokenizer and noun chunking (test_tokenizer.py, test_noun_chunking.py, etc.). Passed all 58 pytest spacy/tests/lang/ht tests that I've created.

    Basic tokenizer rules adapted for Haitian Creole orthography and informal contractions.

    Custom like_num atrribute supporting Haitian number formats (e.g., "3yèm").

    Support for common informal apostrophe usage (e.g., "m'ap", "n'ap", "di'm").

    Ensured no breakages in other language modules.

    Followed spaCy coding style (PEP8, Black).

This provides a foundation for Haitian Creole NLP development using spaCy.
2025-05-28 17:23:38 +02:00
Martin Schorfmann e8f40e2169 Correct API docs for Span.lemma_, Vocab.to_bytes and Vectors.__init__ (#13436)
* Correct code example for Span.lemma_ in API Docs (#13405)

* Correct documented return type of Vocab.to_bytes in API docs

* Correct wording for Vectors.__init__ in API docs
2025-05-28 17:22:50 +02:00
BLKSerene 7b1d6e58ff Remove dependency on langcodes (#13760)
This PR removes the dependency on langcodes introduced in #9342.

While the introduction of langcodes allows a significantly wider range of language codes, there are some unexpected side effects:

    zh-Hant (Traditional Chinese) should be mapped to zh intead of None, as spaCy's Chinese model is based on pkuseg which supports tokenization of both Simplified and Traditional Chinese.
    Since it is possible that spaCy may have a model for Norwegian Nynorsk in the future, mapping no (macrolanguage Norwegian) to nb (Norwegian Bokmål) might be misleading. In that case, the user should be asked to specify nb or nn (Norwegian Nynorsk) specifically or consult the doc.
    Same as above for regional variants of languages such as en_gb and en_us.

Overall, IMHO, introducing an extra dependency just for the conversion of language codes is an overkill. It is possible that most user just need the conversion between 2/3-letter ISO codes and a simple dictionary lookup should suffice.

With this PR, ISO 639-1 and ISO 639-3 codes are supported. ISO 639-2/B (bibliographic codes which are not favored and used in ISO 639-3) and deprecated ISO 639-1/2 codes are also supported to maximize backward compatibility.
2025-05-28 17:21:46 +02:00
Matthew Honnibal 864c2f3b51 Format 2025-05-28 17:06:11 +02:00
Matthew Honnibal 75a9d9b9ad Test and fix issue13769 2025-05-28 17:04:23 +02:00
Ilie bec546cec0 Add TeNs plugin (#13800)
Co-authored-by: Ilie Cristian Dorobat <idorobat@cisco.com>
2025-05-27 01:21:07 +02:00
d0ngw 46613e27cf fix: match hyphenated words to lemmas in index_table (e.g. "co-authored" -> "co-author") (#13816) 2025-05-27 01:20:26 +02:00
omahs b205ff65e6 fix typos (#13813) 2025-05-26 16:05:29 +02:00
BLKSerene 92f1b8cdb4 Switch to typer-slim (#13759) 2025-05-26 16:03:49 +02:00
Matthew Honnibal 4b65aa79ee Add release script 2025-05-22 14:00:48 +02:00
Matthew Honnibal d08f4e3b10 Increment version 2025-05-22 13:58:00 +02:00
Matthew Honnibal 6036f344d3 Remove print statements 2025-05-22 13:56:31 +02:00
Matthew Honnibal 5bebbf7550 Python 3.13 support (#13823)
In order to support Python 3.13, we had to migrate to Cython 3.0. This caused some tricky interaction with our Pydantic usage, because Cython 3 uses the from __future__ import annotations semantics, which causes type annotations to be saved as strings.

The end result is that we can't have Language.factory decorated functions in Cython modules anymore, as the Language.factory decorator expects to inspect the signature of the functions and build a Pydantic model. If the function is implemented in Cython, an error is raised because the type is not resolved.

To address this I've moved the factory functions into a new module, spacy.pipeline.factories. I've added __getattr__ importlib hooks to the previous locations, in case anyone was importing these functions directly. The change should have no backwards compatibility implications.

Along the way I've also refactored the registration of functions for the config. Previously these ran as import-time side-effects, using the registry decorator. I've created instead a new module spacy.registrations. When the registry is accessed it calls a function ensure_populated(), which cases the registrations to occur.

I've made a similar change to the Language.factory registrations in the new spacy.pipeline.factories module.

I want to remove these import-time side-effects so that we can speed up the loading time of the library, which can be especially painful on the CLI. I also find that I'm often working to track down the implementations of functions referenced by strings in the config. Having the registrations all happen in one place will make this easier.

With these changes I've fortunately avoided the need to migrate to Pydantic v2 properly --- we're still using the v1 compatibility shim. We might not be able to hold out forever though: Pydantic (reasonably) aren't actively supporting the v1 shims. I put a lot of work into v2 migration when investigating the 3.13 support, and it's definitely challenging. In any case, it's a relief that we don't have to do the v2 migration at the same time as the Cython 3.0/Python 3.13 support.
2025-05-22 13:47:21 +02:00
Matthew Honnibal 911539e9a4 Update version 2025-05-18 12:18:38 +02:00
Matthew Honnibal 22c1bc785b Replace lte with lt for clarity 2025-05-18 12:18:17 +02:00
Matthew Honnibal cb5e760e91 Fix python version supported 2025-05-18 12:17:23 +02:00
Gunther Cox 87ec2b72a5 Update spaCy Universe entry for ChatterBot to use correct name casing (#13784) 2025-05-12 07:47:50 +02:00
翟持江 aa8de0ed37 Update embeddings-transformers.mdx, update trf_data examples info in <Runtime usage> (#13811) 2025-05-12 07:47:12 +02:00
Adrien Carpentier 98a19df91a docs: fix README.md for compatible Python versions (#13749) 2025-04-11 20:56:52 +02:00
Matthew Honnibal 92bd042502 Allow Python 3.13 2025-04-03 23:15:12 +02:00
Matthew Honnibal d0c705cbc9 Increment version 2025-04-01 09:40:59 +02:00
Matthew Honnibal b3c46c315e Add support for linux-arm 2025-02-03 18:32:23 +01:00
Ines Montani d194f06437 Add live stream to site [ci skip] 2025-02-03 09:42:52 +01:00
Ines Montani 055e07d9cc Update README.md [ci skip] 2025-02-03 09:38:32 +01:00
Ines Montani 8e1c14e977 Add live stream to README [ci skip] 2025-02-03 09:37:48 +01:00
Christine P. Chai 4278182dd0 Change Twitter to X (#13740) [ci skip]
Co-authored-by: Ines Montani <ines@ines.io>
2025-02-03 09:30:21 +01:00
Matthew Honnibal 85cc763006 Fix python version requirement 2025-01-13 18:17:36 +01:00
Matthew Honnibal ba7468e32e Update requirements, fixing windows crashes (#13727)
* Re-enable pretraining test

* Require thinc 8.3.4

* Reformat

* Re-enable test
2025-01-13 16:39:46 +01:00
Matthew Honnibal 311f7cc9fb Set version to v3.8.4 2024-12-11 14:14:08 +01:00
Matthew Honnibal 682140496a Align requirements better 2024-12-11 14:13:51 +01:00
Matthew Honnibal 343f4f21d7 Enable Python 3.13 2024-12-11 14:13:28 +01:00
Matthew Honnibal be0fa812c2 Update cibuildwheel 2024-12-11 13:08:40 +01:00
Matthew Honnibal a6317b3836 Fix allocation of non-transient strings in StringStore (#13713)
* Fix bug in memory-zone code when adding non-transient strings. The error could result in segmentation faults or other memory errors during memory zones if new labels were added to the model.
* Fix handling of new morphological labels within memory zones. Addresses second issue reported in Memory leak of MorphAnalysis object. #13684
2024-12-11 13:06:53 +01:00
Ines Montani 3e30b5bef6 Add spacy-layout [ci skip] 2024-11-19 10:43:40 +01:00
Matthew Honnibal 3ecec1324c Usage page on memory management, explaining memory zones and doc_cleaner (#13643) [ci skip]
Co-authored-by: Ines Montani <ines@ines.io>
2024-10-23 12:42:54 +02:00
Ikko Eltociear Ashimine 15fbf5ef36 docs: update rule-based-matching.mdx (#13665) [ci skip] 2024-10-23 12:07:01 +02:00
Sergei Pashakhin 1ee9a19059 Fix typo (#13657) [ci skip] 2024-10-23 12:06:36 +02:00
thjbdvlt 0d7e57fc3e universe-pipeline-solipCysme-french (#13627) [ci skip] 2024-10-11 11:26:15 +02:00
Ines Montani ae5c3e078d Fix universe.json [ci skip] 2024-10-11 11:24:42 +02:00
Andrei (Andrey) Khropov 8d2902b0e7 Fix misspelling (#13631) [ci skip] 2024-10-11 11:23:12 +02:00
aravind-mc 44d1906453 Update universe.json to add my spaCy online course (#13632) [ci skip] 2024-10-11 11:21:57 +02:00
sam rxh 52a4cb0d14 Fix 'issue template' link in CONTRIBUTING.md (#13587) [ci skip] 2024-10-11 11:20:34 +02:00
Ines Montani 10a6f508ab Fix landing banner links [ci skip] 2024-10-11 11:19:10 +02:00
Matthew Honnibal bda4bb0184 Try disabling pretraining tests to probe windows ci failure (#13646) 2024-10-02 01:01:40 +02:00
Matthew Honnibal 628c973db5 Note minimum python requirement in setup.cfg 2024-10-02 00:49:09 +02:00
Matthew Honnibal e0782c5e4c Merge branch 'master' into v3.8.x 2024-10-01 23:57:48 +02:00
Matthew Honnibal 5230754986 Fix thinc dependncy 2024-10-01 23:49:17 +02:00
Matthew Honnibal 411b70f5f3 Upd requirements 2024-10-01 23:46:54 +02:00
Matthew Honnibal 08705f5a8c Upd tests 2024-10-01 22:57:25 +02:00
Matthew Honnibal 77177d0216 Upd tests workflow 2024-10-01 22:54:12 +02:00
Matthew Honnibal 5196366af5 Upd tests workflow 2024-10-01 22:53:11 +02:00
Matthew Honnibal 29232ad3b5 Upd tests workflow 2024-10-01 22:51:09 +02:00
Matthew Honnibal dd47fbb45f Remove 'apple' extra 2024-10-01 22:24:25 +02:00
Matthew Honnibal 63f1b53c1a Check test failure
tests / Validate (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.9) (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.9) (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.9) (push) Has been cancelled
2024-10-01 16:49:49 +02:00
Matthew Honnibal 0cdcfe56cb Set version to v3.8.2 2024-10-01 16:47:24 +02:00
Matthew Honnibal 924cbc9703 Fix environment variable for test 2024-10-01 16:08:06 +02:00
Matthew Honnibal e1d050517d Fix requirements.txt 2024-10-01 15:56:18 +02:00
Matthew Honnibal 6c038aaae0 Don't disable tests on workflow changes 2024-10-01 15:32:01 +02:00
Matthew Honnibal f0084b9143 Fix matrix in tests 2024-10-01 15:28:22 +02:00
Matthew Honnibal ff81bfb8db Update tests 2024-10-01 13:21:10 +02:00
Matthew Honnibal 9c5b61bdff isort 2024-10-01 12:38:51 +02:00
Matthew Honnibal 725ccbac39 Format 2024-10-01 12:38:02 +02:00
Matthew Honnibal a8837beab7 Set version to v3.8.1 2024-10-01 12:37:11 +02:00
Matthew Honnibal 3a0aadcf86 Update spacy[apple] thinc-apple-ops pin for numpy v2 compatibility 2024-10-01 10:16:35 +02:00
DomHudson a61a1d43cf [Documentation] Replace broken URL in _serialization.mdx (#13641) 2024-09-30 17:45:50 +02:00
Matthew Honnibal 114b4894fb Fix --require-parent default 2024-09-29 15:50:31 +02:00
Matthew Honnibal dec13b4258 Fix inverted cli arg 2024-09-29 15:50:05 +02:00
Matthew Honnibal c03f060527 Allow positive option --require-parent 2024-09-29 14:30:14 +02:00
Matthew Honnibal 6255cb985f Include version constraint in parent package requirement 2024-09-29 14:22:21 +02:00
Matthew Honnibal 3b165a8716 Simplify setting to require parent package 2024-09-29 14:19:10 +02:00
Matthew Honnibal 969832f5d6 Fix package 2024-09-29 14:00:11 +02:00
Matthew Honnibal 8ce53a6bbe Syntax 2024-09-29 13:51:44 +02:00
Matthew Honnibal 6fa0d709d5 Support option to not depend on parent package in spacy package 2024-09-29 13:51:04 +02:00
Matthew Honnibal 5010fcbd3a Fix numpy constant 2024-09-14 13:13:11 +02:00
Matthew Honnibal de4f19f3a3 Fix version 2024-09-14 13:12:44 +02:00
Matthew Honnibal 3d03565498 Replace numpy floats in evaluate and update 2024-09-14 12:55:53 +02:00
Matthew Honnibal 0576a1ff56 Fix numpy floats in meta.json 2024-09-14 12:54:08 +02:00
Matthew Honnibal 2f1e7ed09a Lint 2024-09-14 11:36:27 +02:00
Matthew Honnibal e2dc9b79e1 Format 2024-09-14 11:29:40 +02:00
Matthew Honnibal 3c3d75015b Set version to v3.7.7 2024-09-14 11:27:32 +02:00
Matthew Honnibal 50aa3b5cbe Merge branch 'master' of https://github.com/explosion/spaCy 2024-09-14 11:09:44 +02:00
Matthew Honnibal 8266031454 Merge numpy version update 2024-09-14 11:08:35 +02:00
Matthew Honnibal 8dcc4b8daf Skip running tests on PRs 2024-09-14 11:07:23 +02:00
Matthew Honnibal 3a635d2c94 Try skipping 686 2024-09-14 00:12:49 +02:00
Matthew Honnibal a0ce61f55a Fix thinc pin 2024-09-13 14:21:03 +02:00
Matthew Honnibal 83b4015b36 Remove aarch 2024-09-13 12:35:50 +02:00
Matthew Honnibal 419bfaf6e7 Update cibuildwheel 2024-09-13 10:44:48 +02:00
Matthew Honnibal 69ecb85fad Set version to v3.8.1 2024-09-13 10:43:40 +02:00
Matthew Honnibal b427597fc8 Set version to v3.8.0 2024-09-11 21:32:26 +02:00
Matthew Honnibal 1869a197c9 Try enabling macos-14 for arm builds 2024-09-11 16:06:57 +02:00
Matthew Honnibal c068e1de1b Fix dependencies 2024-09-11 15:57:52 +02:00
Matthew Honnibal 184e508d9c Update numpy pin 2024-09-11 15:57:17 +02:00
William Mattingly 30f1f33e78 Added Date spaCy to universe (#13415) [ci skip]
Co-authored-by: Ines Montani <ines@ines.io>
2024-09-10 14:29:03 +02:00
William Mattingly f1a5ff9dba added spacy whisper to universe (#13418) [ci skip]
Co-authored-by: Ines Montani <ines@ines.io>
2024-09-10 14:28:00 +02:00
William Mattingly c80dacd046 added spacy annoy to universe (#13416) [ci skip]
Co-authored-by: Ines Montani <ines@ines.io>
2024-09-10 14:26:21 +02:00
William Mattingly 7fbbb2002a updated universe for number spacy (#13424) [ci skip]
Co-authored-by: Ines Montani <ines@ines.io>
2024-09-10 14:25:23 +02:00
William Mattingly 89c1774d43 added bagpipes-spacy to universe (#13425) [ci skip]
Co-authored-by: Ines Montani <ines@ines.io>
2024-09-10 14:24:06 +02:00
thjbdvlt 081e4e385d universe-project-presque (#13515) [ci skip]
Co-authored-by: Ines Montani <ines@ines.io>
2024-09-10 14:21:41 +02:00
thjbdvlt 0190e669c5 universe-package-quelquhui (#13514) [ci skip]
Co-authored-by: Ines Montani <ines@ines.io>
2024-09-10 14:17:33 +02:00
Oren Halvani 54dc4ee8fb Added: Constituent-Treelib to: universe.json (#13432) [ci skip]
Co-authored-by: Halvani <>
2024-09-10 14:13:36 +02:00
William Mattingly 5a7ad5572c added gliner-spacy to universe (#13417) [ci skip]
Co-authored-by: Sofie Van Landeghem <svlandeg@users.noreply.github.com>
Co-authored-by: Ines Montani <ines@ines.io>
2024-09-10 14:12:52 +02:00
marinelay b18cc94451 Delete unnecessary method (#13441)
Co-authored-by: marinelay <marinelay@gmail.com>
2024-09-09 20:57:13 +02:00
Matthew Honnibal 4cc3ebe74e Format 2024-09-09 20:56:01 +02:00
Matthew Honnibal a019315534 Fix memory zones 2024-09-09 13:49:41 +02:00
Matthew Honnibal 59ac7e6bdb Format 2024-09-09 11:22:52 +02:00
Matthew Honnibal b65491b641 Set version to v3.8.0.dev0 2024-09-09 11:20:23 +02:00
Matthew Honnibal 1b8d560d0e Support 'memory zones' for user memory management (#13621)
Add a context manage nlp.memory_zone(), which will begin
memory_zone() blocks on the vocab, string store, and potentially
other components.

Example usage:

```
with nlp.memory_zone():
    for text in nlp.pipe(texts):
        do_something(doc)
# do_something(doc) <-- Invalid
```

Once the memory_zone() block expires, spaCy will free any shared
resources that were allocated for the text-processing that occurred
within the memory_zone. If you create Doc objects within a memory
zone, it's invalid to access them once the memory zone is expired.

The purpose of this is that spaCy creates and stores Lexeme objects
in the Vocab that can be shared between multiple Doc objects. It also
interns strings. Normally, spaCy can't know when all Doc objects using
a Lexeme are out-of-scope, so new Lexemes accumulate in the vocab,
causing memory pressure.

Memory zones solve this problem by telling spaCy "okay none of the
documents allocated within this block will be accessed again". This
lets spaCy free all new Lexeme objects and other data that were
created during the block.

The mechanism is general, so memory_zone() context managers can be
added to other components that could benefit from them, e.g. pipeline
components.

I experimented with adding memory zone support to the tokenizer as well,
for its cache. However, this seems unnecessarily complicated. It makes
more sense to just stick a limit on the cache size. This lets spaCy
benefit from the efficiency advantage of the cache better, because
we can maintain a (bounded) cache even if only small batches of
documents are being processed.
2024-09-09 11:19:39 +02:00
ykyogoku 608f65ce40 add Tibetan (#13510) 2024-09-09 11:18:03 +02:00
Muzaffer Cikay acbf2a428f Add Kurdish Kurmanji language (#13561)
* Add Kurdish Kurmanji language

* Add lex_attrs
2024-09-09 11:15:40 +02:00
Mark Liberko 55db9c2e87 Added gd language folder (#13570)
Implemented a foundational Scottish Gaelic (gd) language option with tokenizer_exceptions and stop_words files.
2024-09-09 11:14:09 +02:00
Matthew Honnibal 319e02545c Set version to 3.7.6 2024-08-20 12:16:08 +02:00
Matthew Honnibal a8accc3396 Use cibuildwheel to build wheels (#13603)
* Add workflow files for cibuildwheel

* Add config for cibuildwheel

* Set version for experimental prerelease

* Try updating cython

* Skip 32-bit windows builds

* Revert "Try updating cython"

This reverts commit c1b794ab5c.

* Try to import cibuildwheel settings from previous setup
2024-08-20 12:15:05 +02:00
Ines Montani 8cda27aefa Add case study [ci skip] 2024-06-26 09:41:23 +02:00
Matthew Honnibal f78e5ce732 Disable extra CI 2024-06-21 14:32:00 +02:00
Sofie Van Landeghem a6d0fc3602 Remove typing-extensions from requirements (#13516) 2024-05-31 19:20:46 +02:00
Sofie Van Landeghem 82fc2ecfa5 Bump version to 3.7.5 (#13493) 2024-05-15 12:11:33 +02:00
Sofie Van Landeghem c195ca4f9c fix docs for MorphAnalysis.__contains__ (#13433) 2024-05-02 16:46:41 +02:00
Sofie Van Landeghem d3a232f773 Update LICENSE to include 2024 (#13472) 2024-04-30 09:17:59 +02:00
Sofie Van Landeghem ecd85d2618 Update Typer pin and GH actions (#13471)
* update gh actions

* pin typer upperbound to 1.0.0
2024-04-29 13:28:46 +02:00
Alex Strick van Linschoten 045cd43c3f Fix typos in docs (#13466)
* fix typos

* prettier formatting

---------

Co-authored-by: svlandeg <svlandeg@github.com>
2024-04-29 11:10:17 +02:00
Sofie Van Landeghem 74836524e3 Bump to v5 (#13470) 2024-04-29 10:36:31 +02:00
Sofie Van Landeghem 6d6c10ab9c Fix CI (#13469)
* Remove hardcoded architecture setting

* update classifiers to include Python 3.12
2024-04-29 10:18:07 +02:00
Sofie Van Landeghem 2e2334632b Fix use_gold_ents behaviour for EntityLinker (#13400)
* fix type annotation in docs

* only restore entities after loss calculation

* restore entities of sample in initialization

* rename overfitting function

* fix EL scorer

* Relax test

* fix formatting

* Update spacy/pipeline/entity_linker.py

Co-authored-by: Raphael Mitsch <r.mitsch@outlook.com>

* rename to _ensure_ents

* further rename

* allow for scorer to be None

---------

Co-authored-by: Raphael Mitsch <r.mitsch@outlook.com>
2024-04-16 12:00:22 +02:00
Joe Schiff 2e96797696 Convert properties to decorator syntax (#13390) 2024-04-16 11:51:14 +02:00
Sofie Van Landeghem f5e85fa05a allow weasel 0.4.x (#13409) 2024-04-04 12:55:08 +02:00
Yaseen 21aea59001 Update code.module.sass to make code title sticky (#13379) 2024-03-26 12:15:25 +01:00
Sofie Van Landeghem 4dc5fe5469 Renamed main branch back to v4 for now (#13395)
* Update gputests.yml

* Update slowtests.yml
2024-03-26 09:53:07 +01:00
Ines Montani 1252370f69 Move DocSearch key to env var [ci skip] 2024-03-25 10:17:57 +01:00
Sofie Van Landeghem d410d95b52 remove smart_open requirement as it's taken care of via Weasel (#13391) 2024-03-22 18:21:20 +01:00
Matthew Honnibal 0518c36f04 Sanitize direct download (#13313)
The 'direct' option in 'spacy download' is supposed to only download from our model releases repository. However, users were able to pass in a relative path, allowing download from arbitrary repositories. This meant that a service that sourced strings from user input and which used the direct option would allow users to install arbitrary packages.
2024-02-20 13:17:51 +01:00
Daniël de Kok bff8725f4b Set version to 3.7.4 (#13327) 2024-02-14 14:46:28 +01:00
Daniël de Kok fdfdbcd9f4 Make Language.pipe workers exit cleanly (#13321)
Also warn when any worker exited with a non-zero exit code and modify
test to ensure that workers exit cleanly by default.
2024-02-12 14:39:38 +01:00
Daniël de Kok 14bd9d89a3 Update example that shows model in requirments (#13302)
See #13293.
2024-02-11 19:46:43 +01:00
329 changed files with 10933 additions and 3282 deletions
+1 -1
View File
@@ -10,7 +10,7 @@ SLACK_TOKEN = os.environ.get("SLACK_BOT_TOKEN", "ENV VAR not available!")
DATETIME_FORMAT = "%Y-%m-%dT%H:%M:%SZ"
client = WebClient(SLACK_TOKEN)
github_context = json.loads(sys.argv[1])
github_context = json.loads(sys.stdin.read())
event = github_context['event']
pr_title = event['pull_request']["title"]
+114
View File
@@ -0,0 +1,114 @@
name: Build
on:
push:
tags:
# ytf did they invent their own syntax that's almost regex?
# ** 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@2c98f757f13d112cf73fcf4b627249f1fffb5aae # main
permissions:
contents: write
actions: read
with:
wheel-name-pattern: "spacy-*.whl"
pure-python: false
secrets:
gh-token: ${{ secrets.GITHUB_TOKEN }}
smoke_test:
name: Smoke test
# No checkout here: the jobs below must import the installed wheel, and a
# checked-out source tree in the working directory would shadow it.
needs: build_wheels
runs-on: ubuntu-latest
permissions:
contents: read
steps:
- uses: actions/setup-python@a26af69be951a213d495a4c3e4e4022e16d87065 # v5
with:
python-version: "3.12"
# The release created by build_wheels is a draft, which can't be looked
# up by tag, so pull the wheel from this run's artifacts instead
- uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8
with:
pattern: "cibw-wheels-ubuntu-latest-*"
merge-multiple: true
path: "dist"
- name: Install from wheel
run: |
WHEEL=$(ls dist/spacy-*cp312*manylinux*x86_64*.whl | head -1)
pip install "$WHEEL"
- name: Test import
run: python -c "import spacy; print('spacy==' + spacy.__version__)"
- name: Download and load model
run: |
python -m spacy download en_core_web_sm
python -c "
import spacy
nlp = spacy.load('en_core_web_sm')
doc = nlp('Apple is looking at buying U.K. startup for \$1 billion')
assert len(doc.ents) > 0, 'No entities found'
print('Model load OK:', nlp.meta['name'], '@', nlp.meta['version'])
print('Entities:', [(ent.text, ent.label_) for ent in doc.ents])
"
upgrade_test:
name: Upgrade test
# No checkout here: the jobs below must import the installed wheel, and a
# checked-out source tree in the working directory would shadow it.
needs: build_wheels
runs-on: ubuntu-latest
permissions:
contents: read
steps:
- uses: actions/setup-python@a26af69be951a213d495a4c3e4e4022e16d87065 # v5
with:
python-version: "3.12"
- name: Install previous spaCy version
run: |
# strip the release-v/prerelease-v prefix to get a version specifier;
# click is needed because spacy <=3.8.14 imports it without
# declaring it (#13971) and modern typer no longer pulls it in
pip install "spacy>=3.8.0,<${GITHUB_REF_NAME##*-v}" click || pip install "spacy<4" click
python -m spacy download en_core_web_sm
python -c "
import spacy
nlp = spacy.load('en_core_web_sm')
print('Pre-upgrade:', spacy.__version__, nlp.meta['name'], '@', nlp.meta['version'])
"
# The release created by build_wheels is a draft, which can't be looked
# up by tag, so pull the wheel from this run's artifacts instead
- uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8
with:
pattern: "cibw-wheels-ubuntu-latest-*"
merge-multiple: true
path: "dist"
- name: Upgrade to new version
run: |
WHEEL=$(ls dist/spacy-*cp312*manylinux*x86_64*.whl | head -1)
pip install "$WHEEL"
- name: Test model still loads after upgrade
run: |
python -c "
import spacy
nlp = spacy.load('en_core_web_sm')
doc = nlp('Apple is looking at buying U.K. startup for \$1 billion')
assert len(doc.ents) > 0, 'No entities found after upgrade'
print('Post-upgrade:', spacy.__version__, nlp.meta['name'], '@', nlp.meta['version'])
print('Entities:', [(ent.text, ent.label_) for ent in doc.ents])
"
+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@v3
- 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"
@@ -9,7 +9,7 @@ jobs:
strategy:
fail-fast: false
matrix:
branch: [master, main]
branch: [master, v4]
if: github.repository_owner == 'explosion'
runs-on: ubuntu-latest
steps:
+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@v4
- uses: dessant/lock-threads@1bf7ec25051fe7c00bdd17e6a7cf3d7bfb7dc771 # v5
with:
process-only: 'issues'
issue-inactive-days: '30'
+31
View File
@@ -0,0 +1,31 @@
# The cibuildwheel action triggers on creation of a release, this
# triggers on publication.
# The expected workflow is to create a draft release and let the wheels
# upload, and then hit 'publish', which uploads to PyPi.
on:
release:
types:
- published
permissions: {}
jobs:
upload_pypi:
runs-on: ubuntu-latest
environment:
name: pypi
url: https://pypi.org/p/spacy
permissions:
id-token: write
contents: read
if: github.event_name == 'release' && github.event.action == 'published'
# 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@daf26c55d821e836577a15f77d86ddc078948b05 # v1
with:
tag: ${{ github.event.release.tag_name }}
fileName: '*'
out-file-path: 'dist'
- uses: pypa/gh-action-pypi-publish@ed0c53931b1dc9bd32cbe73a98c7f6766f8a527e # release/v1
@@ -9,12 +9,12 @@ jobs:
strategy:
fail-fast: false
matrix:
branch: [master, main]
branch: [master, v4]
if: github.repository_owner == 'explosion'
runs-on: ubuntu-latest
steps:
- name: Checkout
uses: actions/checkout@v3
uses: actions/checkout@v4
with:
ref: ${{ matrix.branch }}
- name: Get commits from past 24 hours
+5 -10
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@v3
- 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
@@ -30,4 +25,4 @@ jobs:
run: |
pip install slack-sdk==3.17.2 aiohttp==3.8.1
echo "$CHANNEL"
python .github/spacy_universe_alert.py "$GITHUB_CONTEXT"
echo "$GITHUB_CONTEXT" | python .github/spacy_universe_alert.py
+22 -39
View File
@@ -2,6 +2,8 @@ name: tests
on:
push:
tags-ignore:
- '**'
branches-ignore:
- "spacy.io"
- "nightly.spacy.io"
@@ -10,7 +12,6 @@ on:
- "*.md"
- "*.mdx"
- "website/**"
- ".github/workflows/**"
pull_request:
types: [opened, synchronize, reopened, edited]
paths-ignore:
@@ -18,6 +19,9 @@ on:
- "*.mdx"
- "website/**"
permissions:
contents: read
jobs:
validate:
name: Validate
@@ -25,63 +29,40 @@ jobs:
runs-on: ubuntu-latest
steps:
- name: Check out repo
uses: actions/checkout@v3
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"
architecture: x64
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
- 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.12"]
include:
- os: windows-latest
python_version: "3.7"
- os: macos-latest
python_version: "3.8"
- os: ubuntu-latest
python_version: "3.9"
- os: windows-latest
python_version: "3.10"
- os: macos-latest
python_version: "3.11"
python_version: ["3.10", "3.11", "3.12", "3.13", "3.14"]
runs-on: ${{ matrix.os }}
steps:
- name: Check out repo
uses: actions/checkout@v3
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 }}
architecture: x64
- name: Install dependencies
run: |
@@ -115,7 +96,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: |
@@ -159,7 +140,9 @@ jobs:
- name: "Test assemble CLI"
run: |
python -c "import spacy; config = spacy.util.load_config('ner.cfg'); config['components']['ner'] = {'source': 'ca_core_news_sm'}; config.to_disk('ner_source_sm.cfg')"
PYTHONWARNINGS="error,ignore::DeprecationWarning" python -m spacy assemble ner_source_sm.cfg output_dir
python -m spacy assemble ner_source_sm.cfg output_dir
env:
PYTHONWARNINGS: "error,ignore::DeprecationWarning"
if: matrix.python_version == '3.9'
- name: "Test assemble CLI vectors warning"
@@ -174,7 +157,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"
+5 -3
View File
@@ -13,6 +13,9 @@ on:
paths:
- "website/meta/universe.json"
permissions:
contents: read
jobs:
validate:
name: Validate
@@ -20,13 +23,12 @@ jobs:
runs-on: ubuntu-latest
steps:
- name: Check out repo
uses: actions/checkout@v3
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"
architecture: x64
- name: Validate website/meta/universe.json
run: |
+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
+3 -3
View File
@@ -35,7 +35,7 @@ so that more people can benefit from it.
When opening an issue, use a **descriptive title** and include your
**environment** (operating system, Python version, spaCy version). Our
[issue template](https://github.com/explosion/spaCy/issues/new) helps you
[issue templates](https://github.com/explosion/spaCy/issues/new/choose) help you
remember the most important details to include. If you've discovered a bug, you
can also submit a [regression test](#fixing-bugs) straight away. When you're
opening an issue to report the bug, simply refer to your pull request in the
@@ -449,8 +449,8 @@ and plugins in spaCy v3.0, and we can't wait to see what you build with it!
[`spacy`](https://github.com/topics/spacy?o=desc&s=stars) and
[`spacy-extensions`](https://github.com/topics/spacy-extension?o=desc&s=stars)
to make it easier to find. Those are also the topics we're linking to from the
spaCy website. If you're sharing your project on Twitter, feel free to tag
[@spacy_io](https://twitter.com/spacy_io) so we can check it out.
spaCy website. If you're sharing your project on X, feel free to tag
[@spacy_io](https://x.com/spacy_io) so we can check it out.
- Once your extension is published, you can open a
[PR](https://github.com/explosion/spaCy/pulls) to suggest it for the
+1 -1
View File
@@ -1,6 +1,6 @@
The MIT License (MIT)
Copyright (C) 2016-2023 ExplosionAI GmbH, 2016 spaCy GmbH, 2015 Matthew Honnibal
Copyright (C) 2016-2024 ExplosionAI GmbH, 2016 spaCy GmbH, 2015 Matthew Honnibal
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
+1
View File
@@ -4,5 +4,6 @@ include README.md
include pyproject.toml
include spacy/py.typed
recursive-include spacy/cli *.yml
recursive-include spacy/tests *.json
recursive-include licenses *
recursive-exclude spacy *.cpp
+7 -5
View File
@@ -16,7 +16,7 @@ model packaging, deployment and workflow management. spaCy is commercial
open-source software, released under the
[MIT license](https://github.com/explosion/spaCy/blob/master/LICENSE).
💫 **Version 3.7 out now!**
💫 **Version 3.8 out now!**
[Check out the release notes here.](https://github.com/explosion/spaCy/releases)
[![tests](https://github.com/explosion/spaCy/actions/workflows/tests.yml/badge.svg)](https://github.com/explosion/spaCy/actions/workflows/tests.yml)
@@ -28,7 +28,6 @@ open-source software, released under the
<br />
[![PyPi downloads](https://static.pepy.tech/personalized-badge/spacy?period=total&units=international_system&left_color=grey&right_color=orange&left_text=pip%20downloads)](https://pypi.org/project/spacy/)
[![Conda downloads](https://img.shields.io/conda/dn/conda-forge/spacy?label=conda%20downloads)](https://anaconda.org/conda-forge/spacy)
[![spaCy on Twitter](https://img.shields.io/twitter/follow/spacy_io.svg?style=social&label=Follow)](https://twitter.com/spacy_io)
## 📖 Documentation
@@ -47,6 +46,7 @@ open-source software, released under the
| 👩‍🏫 **[Online Course]** | Learn spaCy in this free and interactive online course. |
| 📰 **[Blog]** | Read about current spaCy and Prodigy development, releases, talks and more from Explosion. |
| 📺 **[Videos]** | Our YouTube channel with video tutorials, talks and more. |
| 🔴 **[Live Stream]** | Join Matt as he works on spaCy and chat about NLP. |
| 🛠 **[Changelog]** | Changes and version history. |
| 💝 **[Contribute]** | How to contribute to the spaCy project and code base. |
| 👕 **[Swag]** | Support us and our work with unique, custom-designed swag! |
@@ -62,6 +62,7 @@ open-source software, released under the
[universe]: https://spacy.io/universe
[spacy vs code extension]: https://github.com/explosion/spacy-vscode
[videos]: https://www.youtube.com/c/ExplosionAI
[live stream]: https://www.youtube.com/playlist?list=PLBmcuObd5An5_iAxNYLJa_xWmNzsYce8c
[online course]: https://course.spacy.io
[blog]: https://explosion.ai
[project templates]: https://github.com/explosion/projects
@@ -79,13 +80,14 @@ more people can benefit from it.
| Type | Platforms |
| ------------------------------- | --------------------------------------- |
| 🚨 **Bug Reports** | [GitHub Issue Tracker] |
| 🎁 **Feature Requests & Ideas** | [GitHub Discussions] |
| 🎁 **Feature Requests & Ideas** | [GitHub Discussions] · [Live Stream] |
| 👩‍💻 **Usage Questions** | [GitHub Discussions] · [Stack Overflow] |
| 🗯 **General Discussion** | [GitHub Discussions] |
| 🗯 **General Discussion** | [GitHub Discussions] · [Live Stream] |
[github issue tracker]: https://github.com/explosion/spaCy/issues
[github discussions]: https://github.com/explosion/spaCy/discussions
[stack overflow]: https://stackoverflow.com/questions/tagged/spacy
[live stream]: https://www.youtube.com/playlist?list=PLBmcuObd5An5_iAxNYLJa_xWmNzsYce8c
## Features
@@ -115,7 +117,7 @@ For detailed installation instructions, see the
- **Operating system**: macOS / OS X · Linux · Windows (Cygwin, MinGW, Visual
Studio)
- **Python version**: Python 3.7+ (only 64 bit)
- **Python version**: Python >=3.7, <3.13 (only 64 bit)
- **Package managers**: [pip] · [conda] (via `conda-forge`)
[pip]: https://pypi.org/project/spacy/
+5
View File
@@ -0,0 +1,5 @@
Bug fix for model downloading in environments without pip on PATH
## Fixes
- Fix `spacy download` failing in environments where `pip` is not on PATH but is available as a Python module (e.g., some virtual environments and containers)
Executable
+20
View File
@@ -0,0 +1,20 @@
#!/usr/bin/env bash
set -e
# Insist repository is clean
git diff-index --quiet HEAD
version=$(grep "__version__ = " spacy/about.py)
version=${version/__version__ = }
version=${version/\'/}
version=${version/\'/}
version=${version/\"/}
version=${version/\"/}
echo "Pushing release-v"$version
git tag -d release-v$version || true
git push origin :release-v$version || true
git tag release-v$version
git push origin release-v$version
+1 -5
View File
@@ -1,6 +1,2 @@
# build version constraints for use with wheelwright
numpy==1.15.0; python_version=='3.7' and platform_machine!='aarch64'
numpy==1.19.2; python_version=='3.7' and platform_machine=='aarch64'
numpy==1.17.3; python_version=='3.8' and platform_machine!='aarch64'
numpy==1.19.2; python_version=='3.8' and platform_machine=='aarch64'
numpy>=1.25.0; python_version>='3.9'
numpy>=2.0.0,<3.0.0
+67 -6
View File
@@ -1,15 +1,76 @@
[build-system]
requires = [
"setuptools",
"cython>=0.25,<3.0",
"cython>=3.0,<4.0",
"cymem>=2.0.2,<2.1.0",
"preshed>=3.0.2,<3.1.0",
"murmurhash>=0.28.0,<1.1.0",
"thinc>=8.2.2,<8.3.0",
"numpy>=1.15.0; python_version < '3.9'",
"numpy>=1.25.0; python_version >= '3.9'",
"thinc>=8.3.12,<8.4.0",
"numpy>=2.0.0,<3.0.0"
]
build-backend = "setuptools.build_meta"
[tool.isort]
profile = "black"
[tool.cibuildwheel]
build = "*"
skip = "cp39* *-win32 *i686* cp3??t-* *cp310-win_arm64"
test-skip = ""
archs = ["native"]
build-frontend = "default"
config-settings = {}
dependency-versions = "pinned"
environment = { PIP_CONSTRAINT = "build-constraints.txt" }
environment-pass = []
build-verbosity = 0
before-all = "curl https://sh.rustup.rs -sSf | sh -s -- -y --profile minimal --default-toolchain stable"
before-build = "pip install -r requirements.txt && python setup.py clean"
repair-wheel-command = ""
test-command = ""
before-test = ""
test-requires = []
test-extras = []
container-engine = "docker"
# numpy >=2.3 only ships manylinux_2_28 wheels, so the build container must
# be at least that; i686 keeps manylinux2014 (no 2_28 image) but isn't built
manylinux-x86_64-image = "manylinux_2_28"
manylinux-i686-image = "manylinux2014"
manylinux-aarch64-image = "manylinux_2_28"
manylinux-ppc64le-image = "manylinux2014"
manylinux-s390x-image = "manylinux2014"
manylinux-pypy_x86_64-image = "manylinux2014"
manylinux-pypy_i686-image = "manylinux2014"
manylinux-pypy_aarch64-image = "manylinux2014"
musllinux-x86_64-image = "musllinux_1_2"
musllinux-i686-image = "musllinux_1_2"
musllinux-aarch64-image = "musllinux_1_2"
musllinux-ppc64le-image = "musllinux_1_2"
musllinux-s390x-image = "musllinux_1_2"
[tool.cibuildwheel.linux]
repair-wheel-command = "auditwheel repair -w {dest_dir} {wheel}"
[tool.cibuildwheel.macos]
repair-wheel-command = "delocate-wheel --require-archs {delocate_archs} -w {dest_dir} -v {wheel}"
[tool.cibuildwheel.windows]
[tool.cibuildwheel.pyodide]
[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
+15 -17
View File
@@ -3,40 +3,38 @@ 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.2.2,<8.3.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>=0.3.0,<0.10.0
smart-open>=5.2.1,<7.0.0
weasel>=0.1.0,<0.4.0
typer>=0.3.0,<1.0.0
click>=8.2.1,<9.0.0
weasel>=1.0.0,<2.0.0
# Third party dependencies
numpy>=1.15.0; python_version < "3.9"
numpy>=1.19.0; python_version >= "3.9"
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
langcodes>=3.2.0,<4.0.0
# Official Python utilities
setuptools
packaging>=20.0
typing_extensions>=3.7.4.1,<4.5.0; python_version < "3.8"
# Development dependencies
pre-commit>=2.13.0
cython>=0.25,<3.0
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"
# hypothesis >=6.156 ships a Rust extension with no win_arm64 wheels, which
# breaks wheel builds on windows-11-arm (sdist needs maturin)
hypothesis>=3.27.0,<6.156.0
mypy>=1.20.2,<1.21.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==22.3.0
ruff>=0.9.0
cython-lint>=0.15.0
isort>=5.0,<6.0
confection>=1.3.2,<2.0.0
+19 -26
View File
@@ -17,11 +17,12 @@ classifiers =
Operating System :: Microsoft :: Windows
Programming Language :: Cython
Programming Language :: Python :: 3
Programming Language :: Python :: 3.7
Programming Language :: Python :: 3.8
Programming Language :: Python :: 3.9
Programming Language :: Python :: 3.10
Programming Language :: Python :: 3.11
Programming Language :: Python :: 3.12
Programming Language :: Python :: 3.13
Programming Language :: Python :: 3.14
Topic :: Scientific/Engineering
project_urls =
Release notes = https://github.com/explosion/spaCy/releases
@@ -30,18 +31,18 @@ project_urls =
[options]
zip_safe = false
include_package_data = true
python_requires = >=3.7
python_requires = >=3.9,<3.15
# NOTE: This section is superseded by pyproject.toml and will be removed in
# spaCy v4
setup_requires =
cython>=0.25,<3.0
numpy>=1.15.0; python_version < "3.9"
numpy>=1.19.0; python_version >= "3.9"
cython>=3.0,<4.0
numpy>=2.0.0,<3.0.0; python_version < "3.9"
numpy>=2.0.0,<3.0.0; python_version >= "3.9"
# We also need our Cython packages here to compile against
cymem>=2.0.2,<2.1.0
preshed>=3.0.2,<3.1.0
murmurhash>=0.28.0,<1.1.0
thinc>=8.2.2,<8.3.0
thinc>=8.3.12,<8.4.0
install_requires =
# Our libraries
spacy-legacy>=3.0.11,<3.1.0
@@ -49,25 +50,24 @@ 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.2.2,<8.3.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.1.0,<0.4.0
weasel>=1.0.0,<2.0.0
confection>=1.3.2,<2.0.0
# Third-party dependencies
typer>=0.3.0,<0.10.0
smart-open>=5.2.1,<7.0.0
typer>=0.3.0,<1.0.0
click>=8.2.1,<9.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
packaging>=20.0
typing_extensions>=3.7.4.1,<4.5.0; python_version < "3.8"
langcodes>=3.2.0,<4.0.0
[options.entry_points]
console_scripts =
@@ -117,7 +117,7 @@ cuda12x =
cuda-autodetect =
cupy-wheel>=11.0.0,<13.0.0
apple =
thinc-apple-ops>=0.1.0.dev0,<1.0.0
thinc-apple-ops>=1.0.0,<2.0.0
# Language tokenizers with external dependencies
ja =
sudachipy>=0.5.2,!=0.6.1
@@ -133,20 +133,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
+6 -6
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",
}
@@ -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(),
+25 -2
View File
@@ -10,16 +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.7.3"
__version__ = "3.8.15"
__download_url__ = "https://github.com/explosion/spacy-models/releases/download"
__compatibility__ = "https://raw.githubusercontent.com/explosion/spacy-models/master/compatibility.json"
+2
View File
@@ -1,5 +1,7 @@
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
+5 -16
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,
@@ -20,23 +16,18 @@ from typing import (
import srsly
import typer
from click import NoSuchOption
from click.parser import split_arg_string
from thinc.api import Config, ConfigValidationError, require_gpu
from click.shell_completion import split_arg_string
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,7 +59,7 @@ 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)
@@ -225,13 +216,11 @@ def get_git_version(
@overload
def string_to_list(value: str, intify: Literal[False] = ...) -> List[str]:
...
def string_to_list(value: str, intify: Literal[False] = ...) -> List[str]: ...
@overload
def string_to_list(value: str, intify: Literal[True]) -> List[int]:
...
def string_to_list(value: str, intify: Literal[True]) -> List[int]: ...
def string_to_list(value: str, intify: bool = False) -> Union[List[str], List[int]]:
+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")
+27 -12
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"]
@@ -562,7 +579,7 @@ def debug_data(
if "morphologizer" in factory_names:
msg.divider("Morphologizer (POS+Morph)")
label_list = [label for label in gold_train_data["morphs"]]
label_list = tuple(gold_train_data["morphs"])
model_labels = _get_labels_from_model(nlp, "morphologizer")
msg.info(f"{len(label_list)} label(s) in train data")
labels = set(label_list)
@@ -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:
@@ -968,16 +985,14 @@ def _compile_gold(
@overload
def _format_labels(labels: Iterable[str], counts: Literal[False] = False) -> str:
...
def _format_labels(labels: Iterable[str], counts: Literal[False] = False) -> str: ...
@overload
def _format_labels(
labels: Iterable[Tuple[str, int]],
counts: Literal[True],
) -> str:
...
) -> str: ...
def _format_labels(
+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
+15 -7
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}")
@@ -170,7 +178,7 @@ def debug_model(
msg.divider(f"STEP 3 - prediction")
msg.info(str(prediction))
msg.good(f"Succesfully ended analysis - model looks good.")
msg.good(f"Successfully ended analysis - model looks good.")
def _sentences():
+51 -7
View File
@@ -1,5 +1,8 @@
import importlib.util
import shutil
import sys
from typing import Optional, Sequence
from urllib.parse import urljoin
import requests
import typer
@@ -26,8 +29,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"),
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
):
"""
@@ -40,13 +51,14 @@ def download_cli(
DOCS: https://spacy.io/api/cli#download
AVAILABLE PACKAGES: https://spacy.io/models
"""
download(model, direct, sdist, *ctx.args)
download(model, direct, sdist, url, *ctx.args)
def download(
model: str,
direct: bool = False,
sdist: bool = False,
custom_url: Optional[str] = None,
*pip_args,
) -> None:
if (
@@ -63,6 +75,13 @@ def download(
)
pip_args = pip_args + ("--no-deps",)
if direct:
# Reject model names with '/', in order to prevent shenanigans.
if "/" in model:
msg.fail(
title="Model download rejected",
text=f"Cannot download model '{model}'. Models are expected to be file names, not URLs or fragments",
exits=True,
)
components = model.split("-")
model_name = "".join(components[:-1])
version = components[-1]
@@ -79,7 +98,7 @@ def download(
filename = get_model_filename(model_name, version, sdist)
download_model(filename, pip_args)
download_model(filename, pip_args, custom_url)
msg.good(
"Download and installation successful",
f"You can now load the package via spacy.load('{model_name}')",
@@ -151,9 +170,34 @@ def get_latest_version(model: str) -> str:
def download_model(
filename: str, user_pip_args: Optional[Sequence[str]] = None
filename: str,
user_pip_args: Optional[Sequence[str]] = None,
custom_url: Optional[str] = None,
) -> None:
download_url = about.__download_url__ + "/" + filename
# Construct the download URL carefully. We need to make sure we don't
# allow relative paths or other shenanigans to trick us into download
# from outside our own repo.
base_url = custom_url if custom_url else about.__download_url__
# urljoin requires that the path ends with /, or the last path part will be dropped
if not base_url.endswith("/"):
base_url = about.__download_url__ + "/"
download_url = urljoin(base_url, filename)
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 importlib.util.find_spec("pip") is not None:
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
):
"""
+50 -22
View File
@@ -27,19 +27,43 @@ _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
):
"""
Runs prediction trials for a trained model with varying tresholds to maximize
Runs prediction trials for a trained model with varying thresholds to maximize
the specified metric. The search space for the threshold is traversed linearly
from 0 to 1 in `n_trials` steps. Results are displayed in a table on `stdout`
(the corresponding API call to `spacy.cli.find_threshold.find_threshold()`
@@ -81,7 +105,7 @@ def find_threshold(
silent: bool = True,
) -> Tuple[float, float, Dict[float, float]]:
"""
Runs prediction trials for models with varying tresholds to maximize the specified metric.
Runs prediction trials for models with varying thresholds to maximize the specified metric.
model (Union[str, Path]): Pipeline to evaluate. Can be a package or a path to a data directory.
data_path (Path): Path to file with DocBin with docs to use for threshold search.
pipe_name (str): Name of pipe to examine thresholds for.
@@ -157,9 +181,11 @@ def find_threshold(
exits=1,
)
return {
keys[0]: filter_config(config[keys[0]], keys[1:], full_key)
if len(keys) > 1
else config[keys[0]]
keys[0]: (
filter_config(config[keys[0]], keys[1:], full_key)
if len(keys) > 1
else config[keys[0]]
)
}
# Evaluate with varying threshold values.
@@ -181,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)
@@ -216,12 +242,14 @@ def find_threshold(
if len(set(scores.values())) == 1:
wasabi.msg.warn(
title="All scores are identical. Verify that all settings are correct.",
text=""
if (
not isinstance(pipe, MultiLabel_TextCategorizer)
or scores_key in ("cats_macro_f", "cats_micro_f")
)
else "Use `cats_macro_f` or `cats_micro_f` when optimizing the threshold for `textcat_multilabel`.",
text=(
""
if (
not isinstance(pipe, MultiLabel_TextCategorizer)
or scores_key in ("cats_macro_f", "cats_micro_f")
)
else "Use `cats_macro_f` or `cats_micro_f` when optimizing the threshold for `textcat_multilabel`."
),
)
else:
+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
):
"""
+66 -16
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,
@@ -195,9 +243,11 @@ def init_config(
"Pipeline": ", ".join(pipeline),
"Optimize for": optimize,
"Hardware": variables["hardware"].upper(),
"Transformer": template_vars.transformer.get("name") # type: ignore[attr-defined]
if template_vars.use_transformer # type: ignore[attr-defined]
else None,
"Transformer": (
template_vars.transformer.get("name") # type: ignore[attr-defined]
if template_vars.use_transformer # type: ignore[attr-defined]
else None
),
}
msg.info("Generated config template specific for your use case")
for label, value in use_case.items():
+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
+69 -18
View File
@@ -21,15 +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"),
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
):
"""
@@ -60,6 +101,7 @@ def package_cli(
create_sdist=create_sdist,
create_wheel=create_wheel,
force=force,
require_parent=require_parent,
silent=False,
)
@@ -74,6 +116,7 @@ def package(
create_meta: bool = False,
create_sdist: bool = True,
create_wheel: bool = False,
require_parent: bool = False,
force: bool = False,
silent: bool = True,
) -> None:
@@ -113,7 +156,7 @@ def package(
if not meta_path.exists() or not meta_path.is_file():
msg.fail("Can't load pipeline meta.json", meta_path, exits=1)
meta = srsly.read_json(meta_path)
meta = get_meta(input_dir, meta)
meta = get_meta(input_dir, meta, require_parent=require_parent)
if meta["requirements"]:
msg.good(
f"Including {len(meta['requirements'])} package requirement(s) from "
@@ -186,6 +229,7 @@ def package(
imports.append(code_path.stem)
shutil.copy(str(code_path), str(package_path))
create_file(main_path / "meta.json", srsly.json_dumps(meta, indent=2))
create_file(main_path / "setup.py", TEMPLATE_SETUP)
create_file(main_path / "MANIFEST.in", TEMPLATE_MANIFEST)
init_py = TEMPLATE_INIT.format(
@@ -302,6 +346,8 @@ def get_third_party_dependencies(
modules.add(func_info["module"].split(".")[0]) # type: ignore[union-attr]
dependencies = []
for module_name in modules:
if module_name == about.__title__:
continue
if module_name in distributions:
dist = distributions.get(module_name)
if dist:
@@ -332,7 +378,9 @@ def create_file(file_path: Path, contents: str) -> None:
def get_meta(
model_path: Union[str, Path], existing_meta: Dict[str, Any]
model_path: Union[str, Path],
existing_meta: Dict[str, Any],
require_parent: bool = False,
) -> Dict[str, Any]:
meta: Dict[str, Any] = {
"lang": "en",
@@ -361,6 +409,8 @@ def get_meta(
existing_reqs = [util.split_requirement(req)[0] for req in meta["requirements"]]
reqs = get_third_party_dependencies(nlp.config, exclude=existing_reqs)
meta["requirements"].extend(reqs)
if require_parent and about.__title__ not in meta["requirements"]:
meta["requirements"].append(about.__title__ + meta["spacy_version"])
return meta
@@ -400,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")
@@ -459,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
]
@@ -478,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
@@ -535,8 +583,11 @@ def list_files(data_dir):
def list_requirements(meta):
parent_package = meta.get('parent_package', 'spacy')
requirements = [parent_package + meta['spacy_version']]
# Up to version 3.7, we included the parent package
# in requirements by default. This behaviour is removed
# in 3.8, with a setting to include the parent package in
# the requirements list in the meta if desired.
requirements = []
if 'setup_requires' in meta:
requirements += meta['setup_requires']
if 'requirements' in meta:
+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
):
"""
+10 -3
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
):
"""
@@ -59,7 +66,7 @@ def profile(model: str, inputs: Optional[Path] = None, n_texts: int = 10000) ->
with msg.loading("Loading IMDB dataset via ml_datasets..."):
imdb_train, _ = ml_datasets.imdb(train_limit=n_texts, dev_limit=0)
texts, _ = zip(*imdb_train)
texts = [text for text, _ in imdb_train]
msg.info(f"Loaded IMDB dataset and using {n_texts} examples")
with msg.loading(f"Loading pipeline '{model}'..."):
nlp = load_model(model)
+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
):
"""
+4 -1
View File
@@ -1,4 +1,5 @@
"""Helpers for Python and platform compatibility."""
import sys
from thinc.util import copy_array
@@ -34,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
+2 -1
View File
@@ -4,6 +4,7 @@ spaCy's built in visualization suite for dependencies and named entities.
DOCS: https://spacy.io/api/top-level#displacy
USAGE: https://spacy.io/usage/visualizers
"""
import warnings
from typing import Any, Callable, Dict, Iterable, Optional, Union
@@ -66,7 +67,7 @@ def render(
if jupyter or (jupyter is None and is_in_jupyter()):
# return HTML rendered by IPython display()
# See #4840 for details on span wrapper to disable mathjax
from IPython.core.display import HTML, display
from IPython.display import HTML, display
return display(HTML('<span class="tex2jax_ignore">{}</span>'.format(html)))
return html
+1 -1
View File
@@ -388,7 +388,7 @@ class DependencyRenderer:
lang=self.lang,
)
def render_word(self, text: str, tag: str, lemma: str, i: int) -> str:
def render_word(self, text: str, tag: str, lemma: Optional[str], i: int) -> str:
"""Render individual word.
text (str): Word text.
+1
View File
@@ -220,6 +220,7 @@ class Warnings(metaclass=ErrorsWithCodes):
"key attribute for vectors, configure it through Vectors(attr=) or "
"'spacy init vectors --attr'")
W126 = ("These keys are unsupported: {unsupported}")
W127 = ("Not all `Language.pipe` worker processes completed successfully")
class Errors(metaclass=ErrorsWithCodes):
-1
View File
@@ -5,7 +5,6 @@ Example sentences to test spaCy and its language models.
>>> docs = nlp.pipe(sentences)
"""
sentences = [
"አፕል የዩኬን ጅምር ድርጅት በ 1 ቢሊዮን ዶላር ለመግዛት አስቧል።",
"የራስ ገዝ መኪኖች የኢንሹራንስ ኃላፊነትን ወደ አምራቾች ያዛውራሉ",
+1 -1
View File
@@ -60,7 +60,7 @@ _ordinal_words = [
"አስራ ስምንተኛ",
"አስራ ዘጠነኛ",
"ሃያኛ",
"ሰላሳኛ" "አርባኛ",
"ሰላሳኛአርባኛ",
"አምሳኛ",
"ስድሳኛ",
"ሰባኛ",
-1
View File
@@ -4,7 +4,6 @@ Example sentences to test spaCy and its language models.
>>> docs = nlp.pipe(sentences)
"""
sentences = [
"Bu bir cümlədir.",
"Necəsən?",
+1
View File
@@ -3,6 +3,7 @@ References:
https://github.com/Alir3z4/stop-words - Original list, serves as a base.
https://postvai.com/books/stop-dumi.pdf - Additions to the original list in order to improve it.
"""
STOP_WORDS = set(
"""
а автентичен аз ако ала
-1
View File
@@ -5,5 +5,4 @@ Example sentences to test spaCy and its language models.
>>> docs = nlp.pipe(sentences)
"""
sentences = ["তুই খুব ভালো", "আজ আমরা ডাক্তার দেখতে যাবো", "আমি জানি না "]
+16
View File
@@ -0,0 +1,16 @@
from ...language import BaseDefaults, Language
from .lex_attrs import LEX_ATTRS
from .stop_words import STOP_WORDS
class TibetanDefaults(BaseDefaults):
lex_attr_getters = LEX_ATTRS
stop_words = STOP_WORDS
class Tibetan(Language):
lang = "bo"
Defaults = TibetanDefaults
__all__ = ["Tibetan"]
+15
View File
@@ -0,0 +1,15 @@
"""
Example sentences to test spaCy and its language models.
>>> from spacy.lang.bo.examples import sentences
>>> docs = nlp.pipe(sentences)
"""
sentences = [
"དོན་དུ་རྒྱ་མཚོ་བླ་མ་ཞེས་བྱ་ཞིང༌།",
"ཏཱ་ལའི་ཞེས་པ་ནི་སོག་སྐད་ཡིན་པ་དེ་བོད་སྐད་དུ་རྒྱ་མཚོའི་དོན་དུ་འཇུག",
"སོག་པོ་ཨལ་ཐན་རྒྱལ་པོས་རྒྱལ་དབང་བསོད་ནམས་རྒྱ་མཚོར་ཆེ་བསྟོད་ཀྱི་མཚན་གསོལ་བ་ཞིག་ཡིན་ཞིང༌།",
"རྗེས་སུ་རྒྱལ་བ་དགེ་འདུན་གྲུབ་དང༌། དགེ་འདུན་རྒྱ་མཚོ་སོ་སོར་ཡང་ཏཱ་ལའི་བླ་མའི་སྐུ་ཕྲེང་དང་པོ་དང༌།",
"གཉིས་པའི་མཚན་དེ་གསོལ་ཞིང༌།༸རྒྱལ་དབང་སྐུ་ཕྲེང་ལྔ་པས་དགའ་ལྡན་ཕོ་བྲང་གི་སྲིད་དབང་བཙུགས་པ་ནས་ཏཱ་ལའི་བླ་མ་ནི་བོད་ཀྱི་ཆོས་སྲིད་གཉིས་ཀྱི་དབུ་ཁྲིད་དུ་གྱུར་ཞིང་།",
"ད་ལྟའི་བར་ཏཱ་ལའི་བླ་མ་སྐུ་ཕྲེང་བཅུ་བཞི་བྱོན་ཡོད།",
]
+65
View File
@@ -0,0 +1,65 @@
from ...attrs import LIKE_NUM
# reference 1: https://en.wikipedia.org/wiki/Tibetan_numerals
_num_words = [
"ཀླད་ཀོར་",
"གཅིག་",
"གཉིས་",
"གསུམ་",
"བཞི་",
"ལྔ་",
"དྲུག་",
"བདུན་",
"བརྒྱད་",
"དགུ་",
"བཅུ་",
"བཅུ་གཅིག་",
"བཅུ་གཉིས་",
"བཅུ་གསུམ་",
"བཅུ་བཞི་",
"བཅུ་ལྔ་",
"བཅུ་དྲུག་",
"བཅུ་བདུན་",
"བཅུ་པརྒྱད",
"བཅུ་དགུ་",
"ཉི་ཤུ་",
"སུམ་ཅུ",
"བཞི་བཅུ",
"ལྔ་བཅུ",
"དྲུག་ཅུ",
"བདུན་ཅུ",
"བརྒྱད་ཅུ",
"དགུ་བཅུ",
"བརྒྱ་",
"སྟོང་",
"ཁྲི་",
"ས་ཡ་",
" བྱེ་བ་",
"དུང་ཕྱུར་",
"ཐེར་འབུམ་",
"ཐེར་འབུམ་ཆེན་པོ་",
"ཁྲག་ཁྲིག་",
"ཁྲག་ཁྲིག་ཆེན་པོ་",
]
def like_num(text):
"""
Check if text resembles a number
"""
if text.startswith(("+", "-", "±", "~")):
text = text[1:]
text = text.replace(",", "").replace(".", "")
if text.isdigit():
return True
if text.count("/") == 1:
num, denom = text.split("/")
if num.isdigit() and denom.isdigit():
return True
if text in _num_words:
return True
return False
LEX_ATTRS = {LIKE_NUM: like_num}
+198
View File
@@ -0,0 +1,198 @@
# Source: https://zenodo.org/records/10148636
STOP_WORDS = set(
"""
འི་
དུ་
གིས་
སོགས་
ཏེ
གི་
རྣམས་
ནི
ཀུན་
ཡི་
འདི
ཀྱི་
སྙེད་
པས་
གཞན་
ཀྱིས་
ཡི
ནི་
དང་
སོགས
ཅིང་
དུ
མི་
སུ་
བཅས་
ཡོངས་
ལས
ཙམ་
གྱིས་
དེ་
ཡང་
མཐའ་དག་
ཏུ་
ཉིད་
ཏེ་
གྱི་
སྤྱི
དེ
ཀ་
ཡིན་
ཞིང་
འདི་
རུང་
རང་
ཞིག་
སྟེ
སྟེ་
ན་རེ
ངམ
ཤིང་
དག་
ཏོ
རེ་
འང་
ཀྱང་
ལགས་པ
ཚུ
དོ
ཡིན་པ
རེ
ན་རེ་
ཨེ་
ཚང་མ
ཐམས་ཅད་
དམ་
འོ་
ཅིག་
གྱིན་
ཡིན
ཁོ་ན་
འམ་
ཀྱིན་
ལོ
ཀྱིས
བས་
ལགས་
ཤིག
གིས
ཀི་
སྣ་ཚོགས་
རྣམས
སྙེད་པ
ཡིས་
གྱི
གི
བམ་
ཤིག་
རེ་རེ་
ནམ
མིན་
ནམ་
ངམ་
རུ་
འགའ་
ཀུན
ཤས་
ཏུ
ཡིས
གིན་
གམ་
འོ
ཡིན་པ་
མིན
ལགས
གྱིས
ཅང་
འགའ
སམ་
ཞིག
འང
ལས་ཆེ་
འཕྲལ་
བར་
རུ
དང
འག
སམ
ཅུང་ཟད་
ཅིག
ཉིད
དུ་མ
ཡིན་བ
འམ
མམ
དམ
དག
ཁོ་ན
ཀྱི
ལམ
ཕྱི་
ནང་
ཙམ
ནོ་
སོ་
རམ་
བོ་
ཨང་
ཕྱི
ཏོ་
ཚོ
ལ་ལ་
ཚོ་
ཅིང
མ་གི་
གེ
གོ
ཡིན་ལུགས་
རོ་
བོ
ལགས་པ་
པས
རབ་
འི
རམ
བས
གཞན
སྙེད་པ་
འབའ་
མཾ་
པོ
ག་
གམ
སྤྱི་
བམ
མོ་
ཙམ་པ་
ཤ་སྟག་
མམ་
རེ་རེ
སྙེད
ཏམ་
ངོ
གྲང་
ཏ་རེ
ཏམ
ཁ་
ངེ་
ཅོག་
རིལ་
ཉུང་ཤས་
གིང་
ཚ་
ཀྱང
""".split()
)
-1
View File
@@ -5,7 +5,6 @@ Example sentences to test spaCy and its language models.
>>> docs = nlp.pipe(sentences)
"""
sentences = [
"Apple està buscant comprar una startup del Regne Unit per mil milions de dòlars",
"Els cotxes autònoms deleguen la responsabilitat de l'assegurança als seus fabricants",
+3 -3
View File
@@ -277,10 +277,10 @@ _currency = (
# These expressions contain various unicode variations, including characters
# used in Chinese (see #1333, #1340, #1351) unless there are cross-language
# conflicts, spaCy's base tokenizer should handle all of those by default
_punct = (
r"… …… , : ; \! \? ¿ ؟ ¡ \( \) \[ \] \{ \} < > _ # \* & 。 ? ! , 、 ; : ~ · । ، ۔ ؛ ٪"
_punct = r"… …… , : ; \! \? ¿ ؟ ¡ \( \) \[ \] \{ \} < > _ # \* & 。 ? ! , 、 ; : ~ · । ، ۔ ؛ ٪"
_quotes = (
r'\' " ” “ ` ‘ ´ ’ ‚ , „ » « 「 」 『 』 ( ) 〔 〕 【 】 《 》 〈 〉 〈 〉 ⟦ ⟧'
)
_quotes = r'\' " ” “ ` ‘ ´ ’ ‚ , „ » « 「 」 『 』 ( ) 〔 〕 【 】 《 》 〈 〉 〈 〉 ⟦ ⟧'
_hyphens = "- — -- --- —— ~"
# Various symbols like dingbats, but also emoji
-1
View File
@@ -4,7 +4,6 @@ Example sentences to test spaCy and its language models.
>>> docs = nlp.pipe(sentences)
"""
sentences = [
"Máma mele maso.",
"Příliš žluťoučký kůň úpěl ďábelské ódy.",
+1
View File
@@ -2,6 +2,7 @@
Tokenizer Exceptions.
Source: https://forkortelse.dk/ and various others.
"""
from ...symbols import NORM, ORTH
from ...util import update_exc
from ..tokenizer_exceptions import BASE_EXCEPTIONS
-1
View File
@@ -5,7 +5,6 @@ Example sentences to test spaCy and its language models.
>>> docs = nlp.pipe(sentences)
"""
sentences = [
"Die ganze Stadt ist ein Startup: Shenzhen ist das Silicon Valley für Hardware-Firmen",
"Wie deutsche Startups die Technologie vorantreiben wollen: Künstliche Intelligenz",
-1
View File
@@ -5,7 +5,6 @@ Example sentences to test spaCy and its language models.
>>> docs = nlp.pipe(sentences)
"""
sentences = [
"Z tym stwori so wuměnjenje a zakład za dalše wobdźěłanje přez analyzu tekstoweje struktury a semantisku anotaciju a z tym tež za tu předstajenu digitalnu online-wersiju.",
"Mi so tu jara derje spodoba.",
-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}"},
-1
View File
@@ -5,7 +5,6 @@ Example sentences to test spaCy and its language models.
>>> docs = nlp.pipe(sentences)
"""
sentences = [
"Apple is looking at buying U.K. startup for $1 billion",
"Autonomous cars shift insurance liability toward manufacturers",
-1
View File
@@ -5,7 +5,6 @@ Example sentences to test spaCy and its language models.
>>> docs = nlp.pipe(sentences)
"""
sentences = [
"Apple está buscando comprar una startup del Reino Unido por mil millones de dólares.",
"Los coches autónomos delegan la responsabilidad del seguro en sus fabricantes.",
+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:
-1
View File
@@ -5,7 +5,6 @@ Example sentences to test spaCy and its language models.
>>> docs = nlp.pipe(sentences)
"""
sentences = [
"این یک جمله نمونه می باشد.",
"قرار ما، امروز ساعت ۲:۳۰ بعدازظهر هست!",
+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))
+2 -2
View File
@@ -100,9 +100,9 @@ conj_contraction_negations = [
("eivat", "eivät"),
("eivät", "eivät"),
]
for (base_lower, base_norm) in conj_contraction_bases:
for base_lower, base_norm in conj_contraction_bases:
for base in [base_lower, base_lower.title()]:
for (suffix, suffix_norm) in conj_contraction_negations:
for suffix, suffix_norm in conj_contraction_negations:
_exc[base + suffix] = [
{ORTH: base, NORM: base_norm},
{ORTH: suffix, NORM: suffix_norm},
-1
View File
@@ -5,7 +5,6 @@ Example sentences to test spaCy and its language models.
>>> docs = nlp.pipe(sentences)
"""
sentences = [
"Apple cherche à acheter une start-up anglaise pour 1 milliard de dollars",
"Les voitures autonomes déplacent la responsabilité de l'assurance vers les constructeurs",
+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
+18
View File
@@ -0,0 +1,18 @@
from typing import Optional
from ...language import BaseDefaults, Language
from .stop_words import STOP_WORDS
from .tokenizer_exceptions import TOKENIZER_EXCEPTIONS
class ScottishDefaults(BaseDefaults):
tokenizer_exceptions = TOKENIZER_EXCEPTIONS
stop_words = STOP_WORDS
class Scottish(Language):
lang = "gd"
Defaults = ScottishDefaults
__all__ = ["Scottish"]
+386
View File
@@ -0,0 +1,386 @@
STOP_WORDS = set(
"""
'ad
'ar
'd # iad
'g # ag
'ga
'gam
'gan
'gar
'gur
'm # am
'n # an
'n seo
'na
'nad
'nam
'nan
'nar
'nuair
'nur
's
'sa
'san
'sann
'se
'sna
a
a'
a'd # agad
a'm # agam
a-chèile
a-seo
a-sin
a-siud
a chionn
a chionn 's
a chèile
a chéile
a dh'
a h-uile
a seo
ac' # aca
aca
aca-san
acasan
ach
ag
agad
agad-sa
agads'
agadsa
agaibh
agaibhse
againn
againne
agam
agam-sa
agams'
agamsa
agus
aice
aice-se
aicese
aig
aig' # aige
aige
aige-san
aigesan
air
air-san
air neo
airsan
am
an
an seo
an sin
an siud
an uair
ann
ann a
ann a'
ann a shin
ann am
ann an
annad
annam
annam-s'
annamsa
anns
anns an
annta
aon
ar
as
asad
asda
asta
b'
bho
bhon
bhuaidhe # bhuaithe
bhuainn
bhuaipe
bhuaithe
bhuapa
bhur
brì
bu
c'à
car son
carson
cha
chan
chionn
choir
chon
chun
chèile
chéile
chòir
cia mheud
ciamar
co-dhiubh
cuide
cuin
cuin'
cuine
'
càil
càit
càit'
càite
cò mheud
d'
da
de
dh'
dha
dhaibh
dhaibh-san
dhaibhsan
dhan
dhasan
dhe
dhen
dheth
dhi
dhiom
dhiot
dhith
dhiubh
dhomh
dhomh-s'
dhomhsa
dhu'sa # dhut-sa
dhuibh
dhuibhse
dhuinn
dhuinne
dhuit
dhut
dhutsa
dhut-sa
dhà
dhà-san
dhàsan
dhòmhsa
diubh
do
docha
don
dè mar
dé mar
dòch'
dòcha
e
eadar
eatarra
eatorra
eile
esan
fa
far
feud
fhad
fheudar
fhearr
fhein
fheudar
fheàrr
fhèin
fhéin
fhìn
fo
fodha
fodhainn
foipe
fon
fèin
ga
gach
gam
gan
ge brith
ged
gu
gu dè
gu ruige
gun
gur
gus
i
iad
iadsan
innte
is
ise
le
leam
leam-sa
leamsa
leat
leat-sa
leatha
leatsa
leibh
leis
leis-san
leoth'
leotha
leotha-san
linn
m'
m'a
ma
mac
man
mar
mas
mathaid
mi
mis'
mise
mo
mu
mu 'n
mun
mur
mura
mus
na
na b'
na bu
na iad
nach
nad
nam
nan
nar
nas
neo
no
nuair
o
o'n
oir
oirbh
oirbh-se
oirnn
oirnne
oirre
on
orm
orm-sa
ormsa
orra
orra-san
orrasan
ort
os
r'
ri
ribh
rinn
ris
rithe
rithe-se
rium
rium-sa
riums'
riumsa
riut
riuth'
riutha
riuthasan
ro
ro'n
roimh
roimhe
romhainn
romham
romhpa
ron
ruibh
ruinn
ruinne
sa
san
sann
se
seach
seo
seothach
shin
sibh
sibh-se
sibhse
sin
sineach
sinn
sinne
siod
siodach
siud
siudach
sna # ann an
t'
tarsaing
tarsainn
tarsuinn
thar
thoigh
thro
thu
thuc'
thuca
thugad
thugaibh
thugainn
thugam
thugamsa
thuice
thuige
thus'
thusa
timcheall
toigh
toil
tro
tro' # troimh
troimh
troimhe
tron
tu
tusa
uair
ud
ugaibh
ugam-s'
ugam-sa
uice
uige
uige-san
umad
unnta # ann an
ur
urrainn
à
às
àsan
á
ás
è
ì
ò
ó
""".split("\n")
)
File diff suppressed because it is too large Load Diff
-1
View File
@@ -5,7 +5,6 @@ Example sentences to test spaCy and its language models.
>>> docs = nlp.pipe(sentences)
"""
sentences = [
"ἐρᾷ μὲν ἁγνὸς οὐρανὸς τρῶσαι χθόνα, ἔρως δὲ γαῖαν λαμβάνει γάμου τυχεῖν·",
"εὐδαίμων Χαρίτων καὶ Μελάνιππος ἔφυ, θείας ἁγητῆρες ἐφαμερίοις φιλότατος.",
-1
View File
@@ -5,7 +5,6 @@ Example sentences to test spaCy and its language models.
>>> docs = nlp.pipe(sentences)
"""
sentences = [
"લોકશાહી એ સરકારનું એક એવું તંત્ર છે જ્યાં નાગરિકો મત દ્વારા સત્તાનો ઉપયોગ કરે છે.",
"તે ગુજરાત રાજ્યના ધરમપુર શહેરમાં આવેલું હતું",
-1
View File
@@ -5,7 +5,6 @@ Example sentences to test spaCy and its language models.
>>> docs = nlp.pipe(sentences)
"""
sentences = [
"סין מקימה קרן של 440 מיליון דולר להשקעה בהייטק בישראל",
'רה"מ הודיע כי יחרים טקס בחסותו',
-1
View File
@@ -5,7 +5,6 @@ Example sentences to test spaCy and its language models.
>>> docs = nlp.pipe(sentences)
"""
sentences = [
"एप्पल 1 अरब डॉलर के लिए यू.के. स्टार्टअप खरीदने पर विचार कर रहा है।",
"स्वायत्त कारें निर्माताओं की ओर बीमा दायित्व रखतीं हैं।",
+2 -2
View File
@@ -1,5 +1,5 @@
The list of Croatian lemmas was extracted from the reldi-tagger repository (https://github.com/clarinsi/reldi-tagger).
Reldi-tagger is licesned under the Apache 2.0 licence.
Reldi-tagger is licensed under the Apache 2.0 licence.
@InProceedings{ljubesic16-new,
author = {Nikola Ljubešić and Filip Klubička and Željko Agić and Ivo-Pavao Jazbec},
@@ -12,4 +12,4 @@ Reldi-tagger is licesned under the Apache 2.0 licence.
publisher = {European Language Resources Association (ELRA)},
address = {Paris, France},
isbn = {978-2-9517408-9-1}
}
}
-1
View File
@@ -5,7 +5,6 @@ Example sentences to test spaCy and its language models.
>>> docs = nlp.pipe(sentences)
"""
sentences = [
"To běšo wjelgin raźone a jo se wót luźi derje pśiwzeło. Tak som dožywiła wjelgin",
"Jogo pśewóźowarce stej groniłej, až how w serbskich stronach njama Santa Claus nic pytaś.",
+55
View File
@@ -0,0 +1,55 @@
from typing import Callable, Optional
from thinc.api import Model
from ...language import BaseDefaults, Language
from .lemmatizer import HaitianCreoleLemmatizer
from .lex_attrs import LEX_ATTRS
from .punctuation import TOKENIZER_INFIXES, TOKENIZER_PREFIXES, TOKENIZER_SUFFIXES
from .stop_words import STOP_WORDS
from .syntax_iterators import SYNTAX_ITERATORS
from .tag_map import TAG_MAP
from .tokenizer_exceptions import TOKENIZER_EXCEPTIONS
class HaitianCreoleDefaults(BaseDefaults):
tokenizer_exceptions = TOKENIZER_EXCEPTIONS
prefixes = TOKENIZER_PREFIXES
infixes = TOKENIZER_INFIXES
suffixes = TOKENIZER_SUFFIXES
lex_attr_getters = LEX_ATTRS
syntax_iterators = SYNTAX_ITERATORS
stop_words = STOP_WORDS
tag_map = TAG_MAP
class HaitianCreole(Language):
lang = "ht"
Defaults = HaitianCreoleDefaults
@HaitianCreole.factory(
"lemmatizer",
assigns=["token.lemma"],
default_config={
"model": None,
"mode": "rule",
"overwrite": False,
"scorer": {"@scorers": "spacy.lemmatizer_scorer.v1"},
},
default_score_weights={"lemma_acc": 1.0},
)
def make_lemmatizer(
nlp: Language,
model: Optional[Model],
name: str,
mode: str,
overwrite: bool,
scorer: Optional[Callable],
):
return HaitianCreoleLemmatizer(
nlp.vocab, model, name, mode=mode, overwrite=overwrite, scorer=scorer
)
__all__ = ["HaitianCreole"]
+17
View File
@@ -0,0 +1,17 @@
"""
Example sentences to test spaCy and its language models.
>>> from spacy.lang.ht.examples import sentences
>>> docs = nlp.pipe(sentences)
"""
sentences = [
"Apple ap panse achte yon demaraj nan Wayòm Ini pou $1 milya dola",
"Machin otonòm fè responsablite asirans lan ale sou men fabrikan yo",
"San Francisco ap konsidere entèdi robo ki livre sou twotwa yo",
"Lond se yon gwo vil nan Wayòm Ini",
"Kote ou ye?",
"Kilès ki prezidan Lafrans?",
"Ki kapital Etazini?",
"Kile Barack Obama te fèt?",
]
+50
View File
@@ -0,0 +1,50 @@
from typing import List, Tuple
from ...pipeline import Lemmatizer
from ...tokens import Token
class HaitianCreoleLemmatizer(Lemmatizer):
"""
Minimal Haitian Creole lemmatizer.
Returns a word's base form based on rules and lookup,
or defaults to the original form.
"""
def is_base_form(self, token: Token) -> bool:
morph = token.morph.to_dict()
upos = token.pos_.lower()
# Consider unmarked forms to be base
if upos in {"noun", "verb", "adj", "adv"}:
if not morph:
return True
if upos == "noun" and morph.get("Number") == "Sing":
return True
if upos == "verb" and morph.get("VerbForm") == "Inf":
return True
if upos == "adj" and morph.get("Degree") == "Pos":
return True
return False
def rule_lemmatize(self, token: Token) -> List[str]:
string = token.text.lower()
pos = token.pos_.lower()
cache_key = (token.orth, token.pos)
if cache_key in self.cache:
return self.cache[cache_key]
forms = []
# fallback rule: just return lowercased form
forms.append(string)
self.cache[cache_key] = forms
return forms
@classmethod
def get_lookups_config(cls, mode: str) -> Tuple[List[str], List[str]]:
if mode == "rule":
required = ["lemma_lookup", "lemma_rules", "lemma_exc", "lemma_index"]
return (required, [])
return super().get_lookups_config(mode)
+81
View File
@@ -0,0 +1,81 @@
from ...attrs import LIKE_NUM, NORM
# Cardinal numbers in Creole
_num_words = set(
"""
zewo youn en de twa kat senk sis sèt uit nèf dis
onz douz trèz katoz kenz sèz disèt dizwit diznèf
vent trant karant sinkant swasant swasann-dis
san mil milyon milya
""".split()
)
# Ordinal numbers in Creole (some are French-influenced, some simplified)
_ordinal_words = set(
"""
premye dezyèm twazyèm katryèm senkyèm sizyèm sètvyèm uitvyèm nèvyèm dizyèm
onzèm douzyèm trèzyèm katozyèm kenzèm sèzyèm disetyèm dizwityèm diznèvyèm
ventyèm trantyèm karantyèm sinkantyèm swasantyèm
swasann-disyèm santyèm milyèm milyonnyèm milyadyèm
""".split()
)
NORM_MAP = {
"'m": "mwen",
"'w": "ou",
"'l": "li",
"'n": "nou",
"'y": "yo",
"m": "mwen",
"w": "ou",
"l": "li",
"n": "nou",
"y": "yo",
"m": "mwen",
"n": "nou",
"l": "li",
"y": "yo",
"w": "ou",
"t": "te",
"k": "ki",
"p": "pa",
"M": "Mwen",
"N": "Nou",
"L": "Li",
"Y": "Yo",
"W": "Ou",
"T": "Te",
"K": "Ki",
"P": "Pa",
}
def like_num(text):
text = text.strip().lower()
if text.startswith(("+", "-", "±", "~")):
text = text[1:]
text = text.replace(",", "").replace(".", "")
if text.isdigit():
return True
if text.count("/") == 1:
num, denom = text.split("/")
if num.isdigit() and denom.isdigit():
return True
if text in _num_words:
return True
if text in _ordinal_words:
return True
# Handle things like "3yèm", "10yèm", "25yèm", etc.
if text.endswith("yèm") and text[:-3].isdigit():
return True
return False
def norm_custom(text):
return NORM_MAP.get(text, text.lower())
LEX_ATTRS = {
LIKE_NUM: like_num,
NORM: norm_custom,
}
+58
View File
@@ -0,0 +1,58 @@
from ..char_classes import (
ALPHA,
ALPHA_LOWER,
ALPHA_UPPER,
CONCAT_QUOTES,
HYPHENS,
LIST_ELLIPSES,
LIST_ICONS,
LIST_PUNCT,
LIST_QUOTES,
merge_chars,
)
ELISION = "'".replace(" ", "")
_prefixes_elision = "m n l y t k w"
_prefixes_elision += " " + _prefixes_elision.upper()
TOKENIZER_PREFIXES = (
LIST_PUNCT
+ LIST_QUOTES
+ [
r"(?:({pe})[{el}])(?=[{a}])".format(
a=ALPHA, el=ELISION, pe=merge_chars(_prefixes_elision)
)
]
)
TOKENIZER_SUFFIXES = (
LIST_PUNCT
+ LIST_QUOTES
+ LIST_ELLIPSES
+ [
r"(?<=[0-9])%", # numbers like 10%
r"(?<=[0-9])(?:{h})".format(h=HYPHENS), # hyphens after numbers
r"(?<=[{a}])[']".format(a=ALPHA), # apostrophes after letters
r"(?<=[{a}])['][mwlnytk](?=\s|$)".format(a=ALPHA), # contractions
r"(?<=[{a}0-9])\)", # right parenthesis after letter/number
r"(?<=[{a}])\.(?=\s|$)".format(
a=ALPHA
), # period after letter if space or end of string
r"(?<=\))[\.\?!]", # punctuation immediately after right parenthesis
]
)
TOKENIZER_INFIXES = (
LIST_ELLIPSES
+ LIST_ICONS
+ [
r"(?<=[0-9])[+\-\*^](?=[0-9-])",
r"(?<=[{al}{q}])\.(?=[{au}{q}])".format(
al=ALPHA_LOWER, au=ALPHA_UPPER, q=CONCAT_QUOTES
),
r"(?<=[{a}]),(?=[{a}])".format(a=ALPHA),
r"(?<=[{a}0-9])(?:{h})(?=[{a}])".format(a=ALPHA, h=HYPHENS),
r"(?<=[{a}][{el}])(?=[{a}])".format(a=ALPHA, el=ELISION),
]
)
+49
View File
@@ -0,0 +1,49 @@
STOP_WORDS = set(
"""
a ak an ankò ant apre ap atò avan avanlè
byen bò byenke
chak
de depi deja deja
e en epi èske
fò fòk
gen genyen
ki kisa kilès kote koukou konsa konbyen konn konnen kounye kouman
la l laa le lè li lye lò
m m' mwen
nan nap nou n'
ou oumenm
pa paske pami pandan pito pou pral preske pwiske
se selman si sou sòt
ta tap tankou te toujou tou tan tout toutotan twòp tèl
w w' wi wè
y y' yo yon yonn
non o oh eh
sa san si swa si
men mèsi oswa osinon
""".split()
)
# Add common contractions, with and without apostrophe variants
contractions = ["m'", "n'", "w'", "y'", "l'", "t'", "k'"]
for apostrophe in ["'", "", ""]:
for word in contractions:
STOP_WORDS.add(word.replace("'", apostrophe))
+74
View File
@@ -0,0 +1,74 @@
from typing import Iterator, Tuple, Union
from ...errors import Errors
from ...symbols import NOUN, PRON, PROPN
from ...tokens import Doc, Span
def noun_chunks(doclike: Union[Doc, Span]) -> Iterator[Tuple[int, int, int]]:
"""
Detect base noun phrases from a dependency parse for Haitian Creole.
Works on both Doc and Span objects.
"""
# Core nominal dependencies common in Haitian Creole
labels = [
"nsubj",
"obj",
"obl",
"nmod",
"appos",
"ROOT",
]
# Modifiers to optionally include in chunk (to the right)
post_modifiers = ["compound", "flat", "flat:name", "fixed"]
doc = doclike.doc
if not doc.has_annotation("DEP"):
raise ValueError(Errors.E029)
np_deps = {doc.vocab.strings.add(label) for label in labels}
np_mods = {doc.vocab.strings.add(mod) for mod in post_modifiers}
conj_label = doc.vocab.strings.add("conj")
np_label = doc.vocab.strings.add("NP")
adp_pos = doc.vocab.strings.add("ADP")
cc_pos = doc.vocab.strings.add("CCONJ")
prev_end = -1
for i, word in enumerate(doclike):
if word.pos not in (NOUN, PROPN, PRON):
continue
if word.left_edge.i <= prev_end:
continue
if word.dep in np_deps:
right_end = word
# expand to include known modifiers to the right
for child in word.rights:
if child.dep in np_mods:
right_end = child.right_edge
elif child.pos == NOUN:
right_end = child.right_edge
left_index = word.left_edge.i
# Skip prepositions at the start
if word.left_edge.pos == adp_pos:
left_index += 1
prev_end = right_end.i
yield left_index, right_end.i + 1, np_label
elif word.dep == conj_label:
head = word.head
while head.dep == conj_label and head.head.i < head.i:
head = head.head
if head.dep in np_deps:
left_index = word.left_edge.i
if word.left_edge.pos == cc_pos:
left_index += 1
prev_end = word.i
yield left_index, word.i + 1, np_label
SYNTAX_ITERATORS = {"noun_chunks": noun_chunks}
+39
View File
@@ -0,0 +1,39 @@
from spacy.symbols import (
ADJ,
ADP,
ADV,
AUX,
CCONJ,
DET,
INTJ,
NOUN,
NUM,
PART,
PRON,
PROPN,
PUNCT,
SCONJ,
SYM,
VERB,
X,
)
TAG_MAP = {
"NOUN": {"pos": NOUN},
"VERB": {"pos": VERB},
"AUX": {"pos": AUX},
"ADJ": {"pos": ADJ},
"ADV": {"pos": ADV},
"PRON": {"pos": PRON},
"DET": {"pos": DET},
"ADP": {"pos": ADP},
"SCONJ": {"pos": SCONJ},
"CCONJ": {"pos": CCONJ},
"PART": {"pos": PART},
"INTJ": {"pos": INTJ},
"NUM": {"pos": NUM},
"PROPN": {"pos": PROPN},
"PUNCT": {"pos": PUNCT},
"SYM": {"pos": SYM},
"X": {"pos": X},
}
+126
View File
@@ -0,0 +1,126 @@
from spacy.symbols import NORM, ORTH
def make_variants(base, first_norm, second_orth, second_norm):
return {
base: [
{ORTH: base.split("'")[0] + "'", NORM: first_norm},
{ORTH: second_orth, NORM: second_norm},
],
base.capitalize(): [
{
ORTH: base.split("'")[0].capitalize() + "'",
NORM: first_norm.capitalize(),
},
{ORTH: second_orth, NORM: second_norm},
],
}
TOKENIZER_EXCEPTIONS = {"Dr.": [{ORTH: "Dr."}]}
# Apostrophe forms
TOKENIZER_EXCEPTIONS.update(make_variants("m'ap", "mwen", "ap", "ap"))
TOKENIZER_EXCEPTIONS.update(make_variants("n'ap", "nou", "ap", "ap"))
TOKENIZER_EXCEPTIONS.update(make_variants("l'ap", "li", "ap", "ap"))
TOKENIZER_EXCEPTIONS.update(make_variants("y'ap", "yo", "ap", "ap"))
TOKENIZER_EXCEPTIONS.update(make_variants("m'te", "mwen", "te", "te"))
TOKENIZER_EXCEPTIONS.update(make_variants("m'pral", "mwen", "pral", "pral"))
TOKENIZER_EXCEPTIONS.update(make_variants("w'ap", "ou", "ap", "ap"))
TOKENIZER_EXCEPTIONS.update(make_variants("k'ap", "ki", "ap", "ap"))
TOKENIZER_EXCEPTIONS.update(make_variants("p'ap", "pa", "ap", "ap"))
TOKENIZER_EXCEPTIONS.update(make_variants("t'ap", "te", "ap", "ap"))
# Non-apostrophe contractions (with capitalized variants)
TOKENIZER_EXCEPTIONS.update(
{
"map": [
{ORTH: "m", NORM: "mwen"},
{ORTH: "ap", NORM: "ap"},
],
"Map": [
{ORTH: "M", NORM: "Mwen"},
{ORTH: "ap", NORM: "ap"},
],
"lem": [
{ORTH: "le", NORM: "le"},
{ORTH: "m", NORM: "mwen"},
],
"Lem": [
{ORTH: "Le", NORM: "Le"},
{ORTH: "m", NORM: "mwen"},
],
"lew": [
{ORTH: "le", NORM: "le"},
{ORTH: "w", NORM: "ou"},
],
"Lew": [
{ORTH: "Le", NORM: "Le"},
{ORTH: "w", NORM: "ou"},
],
"nap": [
{ORTH: "n", NORM: "nou"},
{ORTH: "ap", NORM: "ap"},
],
"Nap": [
{ORTH: "N", NORM: "Nou"},
{ORTH: "ap", NORM: "ap"},
],
"lap": [
{ORTH: "l", NORM: "li"},
{ORTH: "ap", NORM: "ap"},
],
"Lap": [
{ORTH: "L", NORM: "Li"},
{ORTH: "ap", NORM: "ap"},
],
"yap": [
{ORTH: "y", NORM: "yo"},
{ORTH: "ap", NORM: "ap"},
],
"Yap": [
{ORTH: "Y", NORM: "Yo"},
{ORTH: "ap", NORM: "ap"},
],
"mte": [
{ORTH: "m", NORM: "mwen"},
{ORTH: "te", NORM: "te"},
],
"Mte": [
{ORTH: "M", NORM: "Mwen"},
{ORTH: "te", NORM: "te"},
],
"mpral": [
{ORTH: "m", NORM: "mwen"},
{ORTH: "pral", NORM: "pral"},
],
"Mpral": [
{ORTH: "M", NORM: "Mwen"},
{ORTH: "pral", NORM: "pral"},
],
"wap": [
{ORTH: "w", NORM: "ou"},
{ORTH: "ap", NORM: "ap"},
],
"Wap": [
{ORTH: "W", NORM: "Ou"},
{ORTH: "ap", NORM: "ap"},
],
"kap": [
{ORTH: "k", NORM: "ki"},
{ORTH: "ap", NORM: "ap"},
],
"Kap": [
{ORTH: "K", NORM: "Ki"},
{ORTH: "ap", NORM: "ap"},
],
"tap": [
{ORTH: "t", NORM: "te"},
{ORTH: "ap", NORM: "ap"},
],
"Tap": [
{ORTH: "T", NORM: "Te"},
{ORTH: "ap", NORM: "ap"},
],
}
)
-1
View File
@@ -5,7 +5,6 @@ Example sentences to test spaCy and its language models.
>>> docs = nlp.pipe(sentences)
"""
sentences = [
"Az Apple egy brit startup vásárlását tervezi 1 milliárd dollár értékben.",
"San Francisco vezetése mérlegeli a járdát használó szállító robotok betiltását.",
+1 -1
View File
@@ -11,7 +11,7 @@ from ..char_classes import (
)
# removing ° from the special icons to keep e.g. 99° as one token
_concat_icons = CONCAT_ICONS.replace("\u00B0", "")
_concat_icons = CONCAT_ICONS.replace("\u00b0", "")
_currency = r"\$¢£€¥฿"
_quotes = CONCAT_QUOTES.replace("'", "")
-1
View File
@@ -4,7 +4,6 @@ Example sentences to test spaCy and its language models.
>>> docs = nlp.pipe(sentences)
"""
sentences = [
"Լոնդոնը Միացյալ Թագավորության մեծ քաղաք է։",
"Ո՞վ է Ֆրանսիայի նախագահը։",
-1
View File
@@ -5,7 +5,6 @@ Example sentences to test spaCy and its language models.
>>> docs = nlp.pipe(sentences)
"""
sentences = [
"Indonesia merupakan negara kepulauan yang kaya akan budaya.",
"Berapa banyak warga yang dibutuhkan saat kerja bakti?",
+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
View File
@@ -5,7 +5,6 @@ Example sentences to test spaCy and its language models.
>>> docs = nlp.pipe(sentences)
"""
sentences = [
"Apple vuole comprare una startup del Regno Unito per un miliardo di dollari",
"Le automobili a guida autonoma spostano la responsabilità assicurativa verso i produttori",
+4 -5
View File
@@ -32,7 +32,6 @@ split_mode = null
"""
@registry.tokenizers("spacy.ja.JapaneseTokenizer")
def create_tokenizer(split_mode: Optional[str] = None):
def japanese_tokenizer_factory(nlp):
return JapaneseTokenizer(nlp.vocab, split_mode=split_mode)
@@ -62,7 +61,7 @@ class JapaneseTokenizer(DummyTokenizer):
zip(*dtokens) if dtokens else [[]] * 7
)
sub_tokens_list = list(sub_tokens_list)
doc = Doc(self.vocab, words=words, spaces=spaces)
doc = Doc(self.vocab, words=list(words), spaces=spaces)
next_pos = None # for bi-gram rules
for idx, (token, dtoken) in enumerate(zip(doc, dtokens)):
token.tag_ = dtoken.tag
@@ -103,9 +102,9 @@ class JapaneseTokenizer(DummyTokenizer):
token.dictionary_form(), # lemma
token.normalized_form(),
token.reading_form(),
sub_tokens_list[idx]
if sub_tokens_list
else None, # user_data['sub_tokens']
(
sub_tokens_list[idx] if sub_tokens_list else None
), # user_data['sub_tokens']
)
for idx, token in enumerate(sudachipy_tokens)
if len(token.surface()) > 0
-1
View File
@@ -5,7 +5,6 @@ Example sentences to test spaCy and its language models.
>>> docs = nlp.pipe(sentences)
"""
sentences = [
"アップルがイギリスの新興企業を10億ドルで購入を検討",
"自動運転車の損害賠償責任、自動車メーカーに一定の負担を求める",

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